Back to index

citadel  8.12
serv_netconfig.c
Go to the documentation of this file.
00001 /*
00002  * This module handles shared rooms, inter-Citadel mail, and outbound
00003  * mailing list processing.
00004  *
00005  * Copyright (c) 2000-2012 by the citadel.org team
00006  *
00007  *  This program is open source software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 3 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License
00018  *  along with this program; if not, write to the Free Software
00019  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020  *
00021  * ** NOTE **   A word on the S_NETCONFIGS semaphore:
00022  * This is a fairly high-level type of critical section.  It ensures that no
00023  * two threads work on the netconfigs files at the same time.  Since we do
00024  * so many things inside these, here are the rules:
00025  *  1. begin_critical_section(S_NETCONFIGS) *before* begin_ any others.
00026  *  2. Do *not* perform any I/O with the client during these sections.
00027  *
00028  */
00029 
00030 /*
00031  * Duration of time (in seconds) after which pending list subscribe/unsubscribe
00032  * requests that have not been confirmed will be deleted.
00033  */
00034 #define EXP   259200 /* three days */
00035 
00036 #include "sysdep.h"
00037 #include <stdlib.h>
00038 #include <unistd.h>
00039 #include <stdio.h>
00040 #include <fcntl.h>
00041 #include <ctype.h>
00042 #include <signal.h>
00043 #include <pwd.h>
00044 #include <errno.h>
00045 #include <sys/stat.h>
00046 #include <sys/types.h>
00047 #include <dirent.h>
00048 #if TIME_WITH_SYS_TIME
00049 # include <sys/time.h>
00050 # include <time.h>
00051 #else
00052 # if HAVE_SYS_TIME_H
00053 #  include <sys/time.h>
00054 # else
00055 #  include <time.h>
00056 # endif
00057 #endif
00058 #ifdef HAVE_SYSCALL_H
00059 # include <syscall.h>
00060 #else 
00061 # if HAVE_SYS_SYSCALL_H
00062 #  include <sys/syscall.h>
00063 # endif
00064 #endif
00065 
00066 #include <sys/wait.h>
00067 #include <string.h>
00068 #include <limits.h>
00069 #include <libcitadel.h>
00070 #include "citadel.h"
00071 #include "server.h"
00072 #include "citserver.h"
00073 #include "support.h"
00074 #include "config.h"
00075 #include "user_ops.h"
00076 #include "database.h"
00077 #include "msgbase.h"
00078 #include "internet_addressing.h"
00079 #include "serv_network.h"
00080 #include "clientsocket.h"
00081 #include "file_ops.h"
00082 #include "citadel_dirs.h"
00083 #include "threads.h"
00084 
00085 #ifndef HAVE_SNPRINTF
00086 #include "snprintf.h"
00087 #endif
00088 
00089 #include "context.h"
00090 #include "netconfig.h"
00091 #include "netspool.h"
00092 #include "ctdl_module.h"
00093 
00094 
00095 
00096 void DeleteNodeConf(void *vNode)
00097 {
00098        NodeConf *Node = (NodeConf*) vNode;
00099        FreeStrBuf(&Node->NodeName);
00100        FreeStrBuf(&Node->Secret);
00101        FreeStrBuf(&Node->Host);
00102        FreeStrBuf(&Node->Port);
00103        free(Node);
00104 }
00105 
00106 NodeConf *NewNode(StrBuf *SerializedNode)
00107 {
00108        const char *Pos = NULL;
00109        NodeConf *Node;
00110 
00111        /* we need at least 4 pipes and some other text so its invalid. */
00112        if (StrLength(SerializedNode) < 8)
00113               return NULL;
00114        Node = (NodeConf *) malloc(sizeof(NodeConf));
00115 
00116        Node->DeleteMe = 0;
00117 
00118        Node->NodeName=NewStrBuf();
00119        StrBufExtract_NextToken(Node->NodeName, SerializedNode, &Pos, '|');
00120 
00121        Node->Secret=NewStrBuf();
00122        StrBufExtract_NextToken(Node->Secret, SerializedNode, &Pos, '|');
00123 
00124        Node->Host=NewStrBuf();
00125        StrBufExtract_NextToken(Node->Host, SerializedNode, &Pos, '|');
00126 
00127        Node->Port=NewStrBuf();
00128        StrBufExtract_NextToken(Node->Port, SerializedNode, &Pos, '|');
00129        return Node;
00130 }
00131 
00132 
00133 /*
00134  * Load or refresh the Citadel network (IGnet) configuration for this node.
00135  */
00136 HashList* load_ignetcfg(void)
00137 {
00138        const char *LinePos;
00139        char       *Cfg;
00140        StrBuf     *Buf;
00141        StrBuf     *LineBuf;
00142        HashList   *Hash;
00143        NodeConf   *Node;
00144 
00145        Cfg =  CtdlGetSysConfig(IGNETCFG);
00146        if ((Cfg == NULL) || IsEmptyStr(Cfg)) {
00147               if (Cfg != NULL)
00148                      free(Cfg);
00149               return NULL;
00150        }
00151 
00152        Hash = NewHash(1, NULL);
00153        Buf = NewStrBufPlain(Cfg, -1);
00154        free(Cfg);
00155        LineBuf = NewStrBufPlain(NULL, StrLength(Buf));
00156        LinePos = NULL;
00157        do
00158        {
00159               StrBufSipLine(LineBuf, Buf, &LinePos);
00160               if (StrLength(LineBuf) != 0) {
00161                      Node = NewNode(LineBuf);
00162                      if (Node != NULL) {
00163                             Put(Hash, SKEY(Node->NodeName), Node, DeleteNodeConf);
00164                      }
00165               }
00166        } while (LinePos != StrBufNOTNULL);
00167        FreeStrBuf(&Buf);
00168        FreeStrBuf(&LineBuf);
00169        return Hash;
00170 }
00171 
00172 void DeleteNetMap(void *vNetMap)
00173 {
00174        NetMap *TheNetMap = (NetMap*) vNetMap;
00175        FreeStrBuf(&TheNetMap->NodeName);
00176        FreeStrBuf(&TheNetMap->NextHop);
00177        free(TheNetMap);
00178 }
00179 
00180 NetMap *NewNetMap(StrBuf *SerializedNetMap)
00181 {
00182        const char *Pos = NULL;
00183        NetMap *NM;
00184 
00185        /* we need at least 3 pipes and some other text so its invalid. */
00186        if (StrLength(SerializedNetMap) < 6)
00187               return NULL;
00188        NM = (NetMap *) malloc(sizeof(NetMap));
00189 
00190        NM->NodeName=NewStrBuf();
00191        StrBufExtract_NextToken(NM->NodeName, SerializedNetMap, &Pos, '|');
00192 
00193        NM->lastcontact = StrBufExtractNext_long(SerializedNetMap, &Pos, '|');
00194 
00195        NM->NextHop=NewStrBuf();
00196        StrBufExtract_NextToken(NM->NextHop, SerializedNetMap, &Pos, '|');
00197 
00198        return NM;
00199 }
00200 
00201 HashList* read_network_map(void)
00202 {
00203        const char *LinePos;
00204        char       *Cfg;
00205        StrBuf     *Buf;
00206        StrBuf     *LineBuf;
00207        HashList   *Hash;
00208        NetMap     *TheNetMap;
00209 
00210        Cfg =  CtdlGetSysConfig(IGNETMAP);
00211        if ((Cfg == NULL) || IsEmptyStr(Cfg)) {
00212               if (Cfg != NULL)
00213                      free(Cfg);
00214               return NULL;
00215        }
00216 
00217        Hash = NewHash(1, NULL);
00218        Buf = NewStrBufPlain(Cfg, -1);
00219        free(Cfg);
00220        LineBuf = NewStrBufPlain(NULL, StrLength(Buf));
00221        LinePos = NULL;
00222        while (StrBufSipLine(Buf, LineBuf, &LinePos))
00223        {
00224               TheNetMap = NewNetMap(LineBuf);
00225               if (TheNetMap != NULL) { /* TODO: is the NodeName Uniq? */
00226                      Put(Hash, SKEY(TheNetMap->NodeName), TheNetMap, DeleteNetMap);
00227               }
00228        }
00229        FreeStrBuf(&Buf);
00230        FreeStrBuf(&LineBuf);
00231        return Hash;
00232 }
00233 
00234 StrBuf *SerializeNetworkMap(HashList *Map)
00235 {
00236        void *vMap;
00237        const char *key;
00238        long len;
00239        StrBuf *Ret = NewStrBuf();
00240        HashPos *Pos = GetNewHashPos(Map, 0);
00241 
00242        while (GetNextHashPos(Map, Pos, &len, &key, &vMap))
00243        {
00244               NetMap *pMap = (NetMap*) vMap;
00245               StrBufAppendBuf(Ret, pMap->NodeName, 0);
00246               StrBufAppendBufPlain(Ret, HKEY("|"), 0);
00247 
00248               StrBufAppendPrintf(Ret, "%ld", pMap->lastcontact, 0);
00249               StrBufAppendBufPlain(Ret, HKEY("|"), 0);
00250 
00251               StrBufAppendBuf(Ret, pMap->NextHop, 0);
00252               StrBufAppendBufPlain(Ret, HKEY("\n"), 0);
00253        }
00254        DeleteHashPos(&Pos);
00255        return Ret;
00256 }
00257 
00258 
00259 /*
00260  * Learn topology from path fields
00261  */
00262 void network_learn_topology(char *node, char *path, HashList *the_netmap, int *netmap_changed)
00263 {
00264        NetMap *pNM = NULL;
00265        void *vptr;
00266        char nexthop[256];
00267        NetMap *nmptr;
00268 
00269        if (GetHash(the_netmap, node, strlen(node), &vptr) && 
00270            (vptr != NULL))/* TODO: is the NodeName Uniq? */
00271        {
00272               pNM = (NetMap*)vptr;
00273               extract_token(nexthop, path, 0, '!', sizeof nexthop);
00274               if (!strcmp(nexthop, ChrPtr(pNM->NextHop))) {
00275                      pNM->lastcontact = time(NULL);
00276                      (*netmap_changed) ++;
00277                      return;
00278               }
00279        }
00280 
00281        /* If we got here then it's not in the map, so add it. */
00282        nmptr = (NetMap *) malloc(sizeof (NetMap));
00283        nmptr->NodeName = NewStrBufPlain(node, -1);
00284        nmptr->lastcontact = time(NULL);
00285        nmptr->NextHop = NewStrBuf ();
00286        StrBufExtract_tokenFromStr(nmptr->NextHop, path, strlen(path), 0, '!');
00287        /* TODO: is the NodeName Uniq? */
00288        Put(the_netmap, SKEY(nmptr->NodeName), nmptr, DeleteNetMap);
00289        (*netmap_changed) ++;
00290 }
00291 
00292 
00293 /*
00294  * Check the network map and determine whether the supplied node name is
00295  * valid.  If it is not a neighbor node, supply the name of a neighbor node
00296  * which is the next hop.  If it *is* a neighbor node, we also fill in the
00297  * shared secret.
00298  */
00299 int is_valid_node(const StrBuf **nexthop,
00300                 const StrBuf **secret,
00301                 StrBuf *node,
00302                 HashList *IgnetCfg,
00303                 HashList *the_netmap)
00304 {
00305        void *vNetMap;
00306        void *vNodeConf;
00307        NodeConf *TheNode;
00308        NetMap *TheNetMap;
00309 
00310        if (StrLength(node) == 0) {
00311               return(-1);
00312        }
00313 
00314        /*
00315         * First try the neighbor nodes
00316         */
00317        if (GetCount(IgnetCfg) == 0) {
00318               syslog(LOG_INFO, "IgnetCfg is empty!\n");
00319               if (nexthop != NULL) {
00320                      *nexthop = NULL;
00321               }
00322               return(-1);
00323        }
00324 
00325        /* try to find a neigbour with the name 'node' */
00326        if (GetHash(IgnetCfg, SKEY(node), &vNodeConf) && 
00327            (vNodeConf != NULL))
00328        {
00329               TheNode = (NodeConf*)vNodeConf;
00330               if (secret != NULL)
00331                      *secret = TheNode->Secret;
00332               return 0;            /* yup, it's a direct neighbor */
00333        }
00334 
00335        /*
00336         * If we get to this point we have to see if we know the next hop
00337         *//* TODO: is the NodeName Uniq? */
00338        if ((GetCount(the_netmap) > 0) &&
00339            (GetHash(the_netmap, SKEY(node), &vNetMap)))
00340        {
00341               TheNetMap = (NetMap*)vNetMap;
00342               if (nexthop != NULL)
00343                      *nexthop = TheNetMap->NextHop;
00344               return(0);
00345        }
00346 
00347        /*
00348         * If we get to this point, the supplied node name is bogus.
00349         */
00350        syslog(LOG_ERR, "Invalid node name <%s>\n", ChrPtr(node));
00351        return(-1);
00352 }
00353 
00354 
00355 void cmd_gnet(char *argbuf)
00356 {
00357        char filename[PATH_MAX];
00358        char buf[SIZ];
00359        FILE *fp;
00360 
00361        if ( (CC->room.QRflags & QR_MAILBOX) && (CC->user.usernum == atol(CC->room.QRname)) ) {
00362               /* users can edit the netconfigs for their own mailbox rooms */
00363        }
00364        else if (CtdlAccessCheck(ac_room_aide)) return;
00365 
00366        assoc_file_name(filename, sizeof filename, &CC->room, ctdl_netcfg_dir);
00367        cprintf("%d Network settings for room #%ld <%s>\n",
00368               LISTING_FOLLOWS,
00369               CC->room.QRnumber, CC->room.QRname);
00370 
00371        fp = fopen(filename, "r");
00372        if (fp != NULL) {
00373               while (fgets(buf, sizeof buf, fp) != NULL) {
00374                      buf[strlen(buf)-1] = 0;
00375                      cprintf("%s\n", buf);
00376               }
00377               fclose(fp);
00378        }
00379 
00380        cprintf("000\n");
00381 }
00382 
00383 
00384 void cmd_snet(char *argbuf) {
00385        char tempfilename[PATH_MAX];
00386        char filename[PATH_MAX];
00387        int TmpFD;
00388        StrBuf *Line;
00389        struct stat StatBuf;
00390        long len;
00391        int rc;
00392 
00393        unbuffer_output();
00394 
00395        if ( (CC->room.QRflags & QR_MAILBOX) && (CC->user.usernum == atol(CC->room.QRname)) ) {
00396               /* users can edit the netconfigs for their own mailbox rooms */
00397        }
00398        else if (CtdlAccessCheck(ac_room_aide)) return;
00399 
00400        len = assoc_file_name(filename, sizeof filename, &CC->room, ctdl_netcfg_dir);
00401        memcpy(tempfilename, filename, len + 1);
00402 
00403        memset(&StatBuf, 0, sizeof(struct stat));
00404        if ((stat(filename, &StatBuf)  == -1) || (StatBuf.st_size == 0))
00405               StatBuf.st_size = 80; /* Not there or empty? guess 80 chars line. */
00406 
00407        sprintf(tempfilename + len, ".%d", CC->cs_pid);
00408        errno = 0;
00409        TmpFD = open(tempfilename, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR);
00410 
00411        if ((TmpFD > 0) && (errno == 0))
00412        {
00413               char *tmp = malloc(StatBuf.st_size * 2);
00414               memset(tmp, ' ', StatBuf.st_size * 2);
00415               rc = write(TmpFD, tmp, StatBuf.st_size * 2);
00416               free(tmp);
00417               if ((rc <= 0) || (rc != StatBuf.st_size * 2))
00418               {
00419                      close(TmpFD);
00420                      cprintf("%d Unable to allocate the space required for %s: %s\n",
00421                             ERROR + INTERNAL_ERROR,
00422                             tempfilename,
00423                             strerror(errno));
00424                      unlink(tempfilename);
00425                      return;
00426               }      
00427               lseek(TmpFD, SEEK_SET, 0);
00428        }
00429        else {
00430               cprintf("%d Unable to allocate the space required for %s: %s\n",
00431                      ERROR + INTERNAL_ERROR,
00432                      tempfilename,
00433                      strerror(errno));
00434               unlink(tempfilename);
00435               return;
00436        }
00437        Line = NewStrBuf();
00438 
00439        cprintf("%d %s\n", SEND_LISTING, tempfilename);
00440 
00441        len = 0;
00442        while (rc = CtdlClientGetLine(Line), 
00443               (rc >= 0))
00444        {
00445               if ((rc == 3) && (strcmp(ChrPtr(Line), "000") == 0))
00446                      break;
00447               StrBufAppendBufPlain(Line, HKEY("\n"), 0);
00448               write(TmpFD, ChrPtr(Line), StrLength(Line));
00449               len += StrLength(Line);
00450        }
00451        FreeStrBuf(&Line);
00452        ftruncate(TmpFD, len);
00453        close(TmpFD);
00454 
00455        /* Now copy the temp file to its permanent location.
00456         * (We copy instead of link because they may be on different filesystems)
00457         */
00458        begin_critical_section(S_NETCONFIGS);
00459        rename(tempfilename, filename);
00460        end_critical_section(S_NETCONFIGS);
00461 }
00462 
00463 /*
00464  * cmd_netp() - authenticate to the server as another Citadel node polling
00465  *           for network traffic
00466  */
00467 void cmd_netp(char *cmdbuf)
00468 {
00469        struct CitContext *CCC = CC;
00470        HashList *working_ignetcfg;
00471        char *node;
00472        StrBuf *NodeStr;
00473        long nodelen;
00474        int v;
00475 
00476        const StrBuf *secret = NULL;
00477        const StrBuf *nexthop = NULL;
00478        char err_buf[SIZ] = "";
00479 
00480        /* Authenticate */
00481        node = CCC->curr_user;
00482        nodelen = extract_token(CCC->curr_user, cmdbuf, 0, '|', sizeof CCC->curr_user);
00483        NodeStr = NewStrBufPlain(node, nodelen);
00484        /* load the IGnet Configuration to check node validity */
00485        working_ignetcfg = load_ignetcfg();
00486        v = is_valid_node(&nexthop, &secret, NodeStr, working_ignetcfg, NULL);
00487        if (v != 0) {
00488               snprintf(err_buf, sizeof err_buf,
00489                      "An unknown Citadel server called \"%s\" attempted to connect from %s [%s].\n",
00490                      node, CC->cs_host, CC->cs_addr
00491               );
00492               syslog(LOG_WARNING, "%s", err_buf);
00493               cprintf("%d authentication failed\n", ERROR + PASSWORD_REQUIRED);
00494               CtdlAideMessage(err_buf, "IGNet Networking.");
00495               DeleteHash(&working_ignetcfg);
00496               FreeStrBuf(&NodeStr);
00497               return;
00498        }
00499 
00500        extract_token(CCC->user.password, cmdbuf, 1, '|', sizeof CCC->user.password);
00501        if (strcasecmp(CCC->user.password, ChrPtr(secret))) {
00502               snprintf(err_buf, sizeof err_buf,
00503                      "A Citadel server at %s [%s] failed to authenticate as network node \"%s\".\n",
00504                      CC->cs_host, CC->cs_addr, node
00505               );
00506               syslog(LOG_WARNING, "%s", err_buf);
00507               cprintf("%d authentication failed\n", ERROR + PASSWORD_REQUIRED);
00508               CtdlAideMessage(err_buf, "IGNet Networking.");
00509               DeleteHash(&working_ignetcfg);
00510               FreeStrBuf(&NodeStr);
00511               return;
00512        }
00513 
00514        if (network_talking_to(node, nodelen, NTT_CHECK)) {
00515               syslog(LOG_WARNING, "Duplicate session for network node <%s>", node);
00516               cprintf("%d Already talking to %s right now\n", ERROR + RESOURCE_BUSY, node);
00517               DeleteHash(&working_ignetcfg);
00518               FreeStrBuf(&NodeStr);
00519               return;
00520        }
00521        nodelen = safestrncpy(CC->net_node, node, sizeof CC->net_node);
00522        network_talking_to(CC->net_node, nodelen, NTT_ADD);
00523        syslog(LOG_NOTICE, "Network node <%s> logged in from %s [%s]\n",
00524               CC->net_node, CC->cs_host, CC->cs_addr
00525        );
00526        cprintf("%d authenticated as network node '%s'\n", CIT_OK, CC->net_node);
00527        DeleteHash(&working_ignetcfg);
00528        FreeStrBuf(&NodeStr);
00529 }
00530 
00531 int netconfig_check_roomaccess(
00532        char *errmsgbuf, 
00533        size_t n,
00534        const char* RemoteIdentifier)
00535 {
00536        SpoolControl *sc;
00537        char filename[SIZ];
00538        int found;
00539 
00540        if (RemoteIdentifier == NULL)
00541        {
00542               snprintf(errmsgbuf, n, "Need sender to permit access.");
00543               return (ERROR + USERNAME_REQUIRED);
00544        }
00545 
00546        assoc_file_name(filename, sizeof filename, &CC->room, ctdl_netcfg_dir);
00547        begin_critical_section(S_NETCONFIGS);
00548        if (!read_spoolcontrol_file(&sc, filename))
00549        {
00550               end_critical_section(S_NETCONFIGS);
00551               snprintf(errmsgbuf, n,
00552                       "This mailing list only accepts posts from subscribers.");
00553               return (ERROR + NO_SUCH_USER);
00554        }
00555        end_critical_section(S_NETCONFIGS);
00556        found = is_recipient (sc, RemoteIdentifier);
00557        free_spoolcontrol_struct(&sc);
00558        if (found) {
00559               return (0);
00560        }
00561        else {
00562               snprintf(errmsgbuf, n,
00563                       "This mailing list only accepts posts from subscribers.");
00564               return (ERROR + NO_SUCH_USER);
00565        }
00566 }
00567 /*
00568  * Module entry point
00569  */
00570 CTDL_MODULE_INIT(netconfig)
00571 {
00572        if (!threading)
00573        {
00574               CtdlRegisterProtoHook(cmd_gnet, "GNET", "Get network config");
00575               CtdlRegisterProtoHook(cmd_snet, "SNET", "Set network config");
00576               CtdlRegisterProtoHook(cmd_netp, "NETP", "Identify as network poller");
00577        }
00578        return "netconfig";
00579 }