]> code.citadel.org Git - citadel.git/blobdiff - citadel/user_ops.c
* Makefile.in, configure.in, chkpwd.c, acconfig.h: support for
[citadel.git] / citadel / user_ops.c
index 569f5f32ed049e8b679aba35de7e55b5ec8299d8..0848a52982d994cebe00f75bd449a4bf1e685278 100644 (file)
@@ -1,12 +1,6 @@
 /* $Id$ */
 
-/* needed to properly enable crypt() stuff on some systems */
-#define _XOPEN_SOURCE
-/* needed for str[n]casecmp() on some systems if the above is defined */
-#define _XOPEN_SOURCE_EXTENDED
-/* needed to enable threads on some systems if the above are defined */
-#define _POSIX_C_SOURCE 199506L
-
+#include "sysdep.h"
 #include <stdlib.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <signal.h>
 #include <pwd.h>
 #include <sys/types.h>
+#include <sys/wait.h>
 #include <sys/time.h>
 #include <string.h>
 #include <syslog.h>
 #include <limits.h>
+#ifdef HAVE_PTHREAD_H
 #include <pthread.h>
+#endif
+#ifndef ENABLE_CHKPWD
+#include "auth.h"
+#endif
 #include "citadel.h"
 #include "server.h"
 #include "database.h"
@@ -32,7 +32,7 @@
 #include "msgbase.h"
 #include "config.h"
 #include "dynloader.h"
-#include "sysdep.h"
+#include "tools.h"
 
 
 /*
@@ -45,10 +45,12 @@ int getuser(struct usersupp *usbuf, char name[]) {
        int a;
        struct cdbdata *cdbus;
 
-       bzero(usbuf, sizeof(struct usersupp));
+       memset(usbuf, 0, sizeof(struct usersupp));
        for (a=0; a<=strlen(name); ++a) {
-               lowercase_name[a] = tolower(name[a]);
+               if (a < sizeof(lowercase_name))
+                       lowercase_name[a] = tolower(name[a]);
                }
+       lowercase_name[sizeof(lowercase_name)-1] = 0;
 
        cdbus = cdb_fetch(CDB_USERSUPP, lowercase_name, strlen(lowercase_name));
        if (cdbus == NULL) {
@@ -87,8 +89,10 @@ void putuser(struct usersupp *usbuf, char *name)
        int a;
 
        for (a=0; a<=strlen(name); ++a) {
-               lowercase_name[a] = tolower(name[a]);
+               if (a < sizeof(lowercase_name))
+                       lowercase_name[a] = tolower(name[a]);
                }
+       lowercase_name[sizeof(lowercase_name)-1] = 0;
 
        cdb_store(CDB_USERSUPP,
                lowercase_name, strlen(lowercase_name),
@@ -105,6 +109,27 @@ void lputuser(struct usersupp *usbuf, char *name) {
        end_critical_section(S_USERSUPP);
        }
 
+/*
+ * Index-generating function used by Ctdl[Get|Set]Relationship
+ */
+int GenerateRelationshipIndex( char *IndexBuf,
+                               long RoomID,
+                               long RoomGen,
+                               long UserID) {
+
+       struct {
+               long iRoomID;
+               long iRoomGen;
+               long iUserID;
+               } TheIndex;
+
+       TheIndex.iRoomID = RoomID;
+       TheIndex.iRoomGen = RoomGen;
+       TheIndex.iUserID = UserID;
+
+       memcpy(IndexBuf, &TheIndex, sizeof(TheIndex));
+       return(sizeof(TheIndex));
+       }
 
 /*
  * Define a relationship between a user and a room
@@ -113,49 +138,26 @@ void CtdlSetRelationship(struct visit *newvisit,
                        struct usersupp *rel_user,
                        struct quickroom *rel_room) {
 
-       struct cdbdata *cdbvisit;
-       struct visit *visits;
-       int num_visits;
-       int a;
-       int replaced = 0;
-
-       cdbvisit = cdb_fetch(CDB_VISIT, &rel_user->usernum, sizeof(long));
-       if (cdbvisit != NULL) {
-               num_visits = cdbvisit->len / sizeof(struct visit);
-               visits = (struct visit *)
-                       malloc(num_visits * sizeof(struct visit));
-               memcpy(visits, cdbvisit->ptr,
-                       (num_visits * sizeof(struct visit)));
-               cdb_free(cdbvisit);
-               }
-       else {
-               num_visits = 0;
-               visits = NULL;
-               }
-
-       /* Replace an existing relationship if possible */
-       if (num_visits > 0) for (a=0; a<num_visits; ++a) {
-               if ( (!strcasecmp(visits[a].v_roomname, rel_room->QRname))
-                  && (visits[a].v_generation == rel_room->QRgen) ) {
-                       memcpy(&visits[a], newvisit, sizeof(struct visit));
-                       replaced = 1;
-                       }
-               }
-
-       /* Otherwise, define a new one */
-       if (replaced == 0) {
-               ++num_visits;
-               visits = realloc(visits, 
-                       (num_visits * sizeof(struct visit)));
-               memcpy(&visits[num_visits-1], newvisit, sizeof(struct visit));
-               }
+       char IndexBuf[32];
+       int IndexLen;
 
-       /* Now write the relationship back to disk */
-       cdb_store(CDB_VISIT,
-               &rel_user->usernum, sizeof(long),
-               visits,
-               (num_visits * sizeof(struct visit)));
-       free(visits);
+       /* We don't use these in Citadel because they're implicit by the
+        * index, but they must be present if the database is exported.
+        */
+        newvisit->v_roomnum = rel_room->QRnumber;
+        newvisit->v_roomgen = rel_room->QRgen;
+        newvisit->v_usernum = rel_user->usernum;
+
+       /* Generate an index */
+       IndexLen = GenerateRelationshipIndex(IndexBuf,
+               rel_room->QRnumber,
+               rel_room->QRgen,
+               rel_user->usernum);
+
+       /* Store the record */
+       cdb_store(CDB_VISIT, IndexBuf, IndexLen,
+               newvisit, sizeof(struct visit)
+               );
        }
 
 /*
@@ -165,95 +167,30 @@ void CtdlGetRelationship(struct visit *vbuf,
                        struct usersupp *rel_user,
                        struct quickroom *rel_room) {
 
+       char IndexBuf[32];
+       int IndexLen;
        struct cdbdata *cdbvisit;
-       struct visit *visits;
-       int num_visits;
-       int a;
 
-       bzero(vbuf, sizeof(struct visit));
-       strcpy(vbuf->v_roomname, rel_room->QRname);
-       vbuf->v_generation = rel_room->QRgen;
+       /* Generate an index */
+       IndexLen = GenerateRelationshipIndex(IndexBuf,
+               rel_room->QRnumber,
+               rel_room->QRgen,
+               rel_user->usernum);
 
-       cdbvisit = cdb_fetch(CDB_VISIT, &rel_user->usernum, sizeof(long));
-       if (cdbvisit != NULL) {
-               if ((num_visits = cdbvisit->len / sizeof(struct visit)) == 0) {
-                       cdb_free(cdbvisit);
-                       return;
-               }
-               visits = (struct visit *)
-                       malloc(num_visits * sizeof(struct visit));
-               memcpy(visits, cdbvisit->ptr,
-                       (num_visits * sizeof(struct visit)));
-               cdb_free(cdbvisit);
-               }
-       else return;
+       /* Clear out the buffer */
+       memset(vbuf, 0, sizeof(struct visit));
 
-       for (a=0; a<num_visits; ++a) {
-       
-               if ( (!strcasecmp(visits[a].v_roomname, rel_room->QRname))
-                  && (visits[a].v_generation == rel_room->QRgen) ) {
-                       memcpy(vbuf, &visits[a], sizeof(struct visit));
-                       }
-               }
-       
-       free(visits);
-       }
-
-
-void PurgeStaleRelationships(void) {
-
-       struct cdbdata *cdbvisit;
-       struct visit *visits;
-       struct quickroom qrbuf;
-       int num_visits;
-       int a, purge;
-
-       cdbvisit = cdb_fetch(CDB_VISIT, &CC->usersupp.usernum, sizeof(long));
+       cdbvisit = cdb_fetch(CDB_VISIT, IndexBuf, IndexLen);
        if (cdbvisit != NULL) {
-               if ((num_visits = cdbvisit->len / sizeof(struct visit)) == 0) {
-                       cdb_free(cdbvisit);
-                       return;
-                       }
-               visits = (struct visit *)
-                       malloc(num_visits * sizeof(struct visit));
-               memcpy(visits, cdbvisit->ptr,
-                       (num_visits * sizeof(struct visit)));
+               memcpy(vbuf, cdbvisit->ptr,
+                       ( (cdbvisit->len > sizeof(struct visit)) ?
+                       sizeof(struct visit) : cdbvisit->len) );
                cdb_free(cdbvisit);
+               return;
                }
-       else return;
-
-       for (a=0; a<num_visits; ++a) {
-               if (getroom(&qrbuf, visits[a].v_roomname)!=0) {
-                       purge = 1;
-                       }
-               else if (qrbuf.QRgen != visits[a].v_generation) {
-                       purge = 1;
-                       }
-               else {
-                       purge = 0;
-                       }
-               lprintf(9, "U/R REL: <%s> <%ld> <%ld> <%d> %s\n",
-                       visits[a].v_roomname,
-                       visits[a].v_generation,
-                       visits[a].v_lastseen,
-                       visits[a].v_flags,
-                       (purge ? "**purging**" : "") );
-
-               if (purge) {
-                       memcpy(&visits[a], &visits[a+1],
-                               (((num_visits-a)-1) * sizeof(struct visit)) );
-                       --num_visits;
-                       }
-
-               }
-       
-       cdb_store(CDB_VISIT, &CC->usersupp.usernum, sizeof(long),
-                       visits, (num_visits * sizeof(struct visit)));
-       free(visits);
        }
 
 
-
 void MailboxName(char *buf, struct usersupp *who, char *prefix) {
        sprintf(buf, "%010ld.%s", who->usernum, prefix);
        }
@@ -292,7 +229,7 @@ int getuserbynumber(struct usersupp *usbuf, long int number)
        cdb_rewind(CDB_USERSUPP);
 
        while(cdbus = cdb_next_item(CDB_USERSUPP), cdbus != NULL) {
-               bzero(usbuf, sizeof(struct usersupp));
+               memset(usbuf, 0, sizeof(struct usersupp));
                memcpy(usbuf, cdbus->ptr,
                        ( (cdbus->len > sizeof(struct usersupp)) ?
                        sizeof(struct usersupp) : cdbus->len) );
@@ -366,7 +303,6 @@ void session_startup(void) {
        CC->fake_postname[0] = '\0';
        CC->fake_hostname[0] = '\0';
        CC->fake_roomname[0] = '\0';
-       CC->last_pager[0] = '\0';
        time(&CC->usersupp.lastcall);
 
        /* If this user's name is the name of the system administrator
@@ -407,12 +343,69 @@ void logout(struct CitContext *who)
        PerformSessionHooks(EVT_LOGOUT);
        }
 
+#ifdef ENABLE_CHKPWD
+/*
+ * an alternate version of validpw() which executes `chkpwd' instead of
+ * verifying the password directly
+ */
+static int validpw(uid_t uid, const char *pass)
+{
+       pid_t pid;
+       int status, pipev[2];
+       char buf[24];
+
+       if (pipe(pipev)) {
+               lprintf(1, "pipe failed (%s): denying autologin access for "
+                          "uid %u\n", strerror(errno), uid);
+               return 0;
+               }
+
+       switch (pid = fork()) {
+           case -1:
+               lprintf(1, "fork failed (%s): denying autologin access for "
+                          "uid %u\n", strerror(errno), uid);
+               close(pipev[0]);
+               close(pipev[1]);
+               return 0;
+
+           case 0:
+               close(pipev[1]);
+               if (dup2(pipev[0], 0) == -1) {
+                       perror("dup2");
+                       exit(1);
+                       }
+               close(pipev[0]);
+
+               execl(BBSDIR "/chkpwd", BBSDIR "/chkpwd", NULL);
+               perror(BBSDIR "/chkpwd");
+               exit(1);
+               }
+
+       close(pipev[0]);
+       write(pipev[1], buf, sprintf(buf, "%u\n", uid));
+       write(pipev[1], pass, strlen(pass));
+       write(pipev[1], "\n", 1);
+       close(pipev[1]);
+
+       while (waitpid(pid, &status, 0) == -1)
+               if (errno != EINTR) {
+                       lprintf(1, "waitpid failed (%s): denying autologin "
+                                  "access for uid %u\n",
+                               strerror(errno), uid);
+                       return 0;
+                       }
+
+       if (WIFEXITED(status) && !WEXITSTATUS(status))
+               return 1;
+
+       return 0;
+       }
+#endif
 
 void cmd_pass(char *buf)
 {
        char password[256];
        int code;
-       struct passwd *p;
 
        extract(password,buf,0);
 
@@ -435,20 +428,17 @@ void cmd_pass(char *buf)
                strproc(CC->usersupp.password);
                code = strcasecmp(CC->usersupp.password,password);
                }
-       else {
-               p = (struct passwd *)getpwuid(CC->usersupp.USuid);
 #ifdef ENABLE_AUTOLOGIN
-               if (p!=NULL) {
-                       if (!strcmp(p->pw_passwd,
-                          (char *)crypt(password,p->pw_passwd))) {
-                               code = 0;
-                               lgetuser(&CC->usersupp, CC->curr_user);
-                               strcpy(CC->usersupp.password, password);
-                               lputuser(&CC->usersupp, CC->curr_user);
-                               }
+       else {
+               if (validpw(CC->usersupp.USuid, password)) {
+                       code = 0;
+                       lgetuser(&CC->usersupp, CC->curr_user);
+                       safestrncpy(CC->usersupp.password, password,
+                                   sizeof CC->usersupp.password);
+                       lputuser(&CC->usersupp, CC->curr_user);
                        }
-#endif
                }
+#endif
 
        if (!code) {
                (CC->logged_in) = 1;
@@ -471,6 +461,8 @@ int purge_user(char pname[]) {
        struct quickroom qrbuf;
        char lowercase_name[32];
        int a;
+       struct CitContext *ccptr;
+       int user_is_logged_in = 0;
 
        for (a=0; a<=strlen(pname); ++a) {
                lowercase_name[a] = tolower(pname[a]);
@@ -481,9 +473,26 @@ int purge_user(char pname[]) {
                return(ERROR+NO_SUCH_USER);
                }
 
-       lprintf(5, "Deleting user <%s>\n", pname);
+       /* Don't delete a user who is currently logged in.  Instead, just
+        * set the access level to 0, and let the account get swept up
+        * during the next purge.
+        */
+       user_is_logged_in = 0;
+       begin_critical_section(S_SESSION_TABLE);
+       for (ccptr=ContextList; ccptr!=NULL; ccptr=ccptr->next) {
+               if (ccptr->usersupp.usernum == usbuf.usernum) {
+                       user_is_logged_in = 1;
+                       }
+               }
+       end_critical_section(S_SESSION_TABLE);
+       if (user_is_logged_in == 1) {
+               lprintf(5, "User <%s> is logged in; not deleting.\n", pname);
+               usbuf.axlevel = 0;
+               putuser(&usbuf, pname);
+               return(1);
+               }
 
-       /* FIX   Don't delete a user who is currently logged in. */
+       lprintf(5, "Deleting user <%s>\n", pname);
 
        /* Perform any purge functions registered by server extensions */
        PerformUserHooks(usbuf.fullname, usbuf.usernum, EVT_PURGEUSER);
@@ -823,6 +832,7 @@ void cmd_forg(void) {
        CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
 
        vbuf.v_flags = vbuf.v_flags | V_FORGET;
+       vbuf.v_flags = vbuf.v_flags & ~V_ACCESS;
 
        CtdlSetRelationship(&vbuf, &CC->usersupp, &CC->quickroom);
        lputuser(&CC->usersupp,CC->curr_user);
@@ -858,7 +868,7 @@ void cmd_gnur(void) {
         */
        cdb_rewind(CDB_USERSUPP);
        while (cdbus = cdb_next_item(CDB_USERSUPP), cdbus != NULL) {
-               bzero(&usbuf, sizeof(struct usersupp));
+               memset(&usbuf, 0, sizeof(struct usersupp));
                memcpy(&usbuf, cdbus->ptr,
                        ( (cdbus->len > sizeof(struct usersupp)) ?
                        sizeof(struct usersupp) : cdbus->len) );
@@ -1002,7 +1012,7 @@ void ForEachUser(void (*CallBack)(struct usersupp *EachUser)) {
        cdb_rewind(CDB_USERSUPP);
 
        while(cdbus = cdb_next_item(CDB_USERSUPP), cdbus != NULL) {
-               bzero(&usbuf, sizeof(struct usersupp));
+               memset(&usbuf, 0, sizeof(struct usersupp));
                memcpy(&usbuf, cdbus->ptr,
                        ( (cdbus->len > sizeof(struct usersupp)) ?
                        sizeof(struct usersupp) : cdbus->len) );
@@ -1126,7 +1136,6 @@ void cmd_regi(void) {
        CitControl.MMflags = CitControl.MMflags | MM_VALID ;
        put_control();
        end_critical_section(S_CONTROL);
-       cprintf("%d *** End of registration.\n",OK);
        }
 
 
@@ -1271,7 +1280,7 @@ void cmd_agup(char *cmdbuf) {
                return;
                }
 
-       cprintf("%d %s|%s|%u|%d|%d|%d|%ld|%d\n", 
+       cprintf("%d %s|%s|%u|%d|%d|%d|%ld|%ld|%d\n", 
                OK,
                usbuf.fullname,
                usbuf.password,
@@ -1280,8 +1289,8 @@ void cmd_agup(char *cmdbuf) {
                usbuf.posted,
                (int)usbuf.axlevel,
                usbuf.usernum,
+               usbuf.lastcall,
                usbuf.USuserpurge);
-
        }
 
 
@@ -1320,7 +1329,10 @@ void cmd_asup(char *cmdbuf) {
                        }
                }
        if (np > 7) {
-               usbuf.USuserpurge = extract_int(cmdbuf, 7);
+               usbuf.lastcall = extract_long(cmdbuf, 7);
+               }
+       if (np > 8) {
+               usbuf.USuserpurge = extract_int(cmdbuf, 8);
                }
 
        lputuser(&usbuf, requested_user);