* Silenced a few warn_unused_result warnings (ongoing process)
[citadel.git] / citadel / user_ops.c
index e7331e8d246f3f2ff5668202ffbde190ece92a57..a4bb7eab40f4bb1ada0ac703ea029800298e46f0 100644 (file)
@@ -50,6 +50,9 @@
 #include "citadel_dirs.h"
 #include "genstamp.h"
 #include "threads.h"
+#include "citadel_ldap.h"
+
+#include "ctdl_module.h"
 
 /* These pipes are used to talk to the chkpwd daemon, which is forked during startup */
 int chkpwd_write_pipe[2];
@@ -59,7 +62,7 @@ int chkpwd_read_pipe[2];
  * makeuserkey() - convert a username into the format used as a database key
  *              (it's just the username converted into lower case)
  */
-static INLINE void makeuserkey(char *key, char *username) {
+INLINE void makeuserkey(char *key, char *username) {
        int i, len;
 
        len = strlen(username);
@@ -187,12 +190,20 @@ int rename_user(char *oldname, char *newname) {
                }
 
                else {          /* Sanity checks succeeded.  Now rename the user. */
-
-                       CtdlLogPrintf(CTDL_DEBUG, "Renaming <%s> to <%s>\n", oldname, newname);
-                       cdb_delete(CDB_USERS, oldnamekey, strlen(oldnamekey));
-                       safestrncpy(usbuf.fullname, newname, sizeof usbuf.fullname);
-                       putuser(&usbuf);
-                       retcode = RENAMEUSER_OK;
+                       if (usbuf.usernum == 0)
+                       {
+                               CtdlLogPrintf (CTDL_DEBUG, "Can not rename user \"Citadel\".\n");
+                               retcode = RENAMEUSER_NOT_FOUND;
+                       } else {
+                               CtdlLogPrintf(CTDL_DEBUG, "Renaming <%s> to <%s>\n", oldname, newname);
+                               cdb_delete(CDB_USERS, oldnamekey, strlen(oldnamekey));
+                               safestrncpy(usbuf.fullname, newname, sizeof usbuf.fullname);
+                               putuser(&usbuf);
+                               cdb_store(CDB_USERSBYNUMBER, &usbuf.usernum, sizeof(long),
+                                       usbuf.fullname, strlen(usbuf.fullname)+1 );
+
+                               retcode = RENAMEUSER_OK;
+                       }
                }
        
        }
@@ -350,33 +361,83 @@ int is_room_aide(void)
 }
 
 /*
- * getuserbynumber()  -  get user by number
- *                    returns 0 if user was found
+ * getuserbynumber() - get user by number
+ *                     returns 0 if user was found
  *
- * WARNING: don't use this function unless you absolutely have to.  It does
- *       a sequential search and therefore is computationally expensive.
+ * Note: fetching a user this way requires one additional database operation.
  */
-int getuserbynumber(struct ctdluser *usbuf, long int number)
+int getuserbynumber(struct ctdluser *usbuf, long number)
 {
-       struct cdbdata *cdbus;
+       struct cdbdata *cdbun;
+       int r;
 
-       cdb_rewind(CDB_USERS);
+       cdbun = cdb_fetch(CDB_USERSBYNUMBER, &number, sizeof(long));
+       if (cdbun == NULL) {
+               CtdlLogPrintf(CTDL_INFO, "User %ld not found\n", number);
+               return(-1);
+       }
 
-       while (cdbus = cdb_next_item(CDB_USERS), cdbus != NULL) {
-               memset(usbuf, 0, sizeof(struct ctdluser));
-               memcpy(usbuf, cdbus->ptr,
-                      ((cdbus->len > sizeof(struct ctdluser)) ?
-                       sizeof(struct ctdluser) : cdbus->len));
-               cdb_free(cdbus);
-               if (usbuf->usernum == number) {
-                       cdb_close_cursor(CDB_USERS);
-                       return (0);
-               }
+       CtdlLogPrintf(CTDL_INFO, "User %ld maps to %s\n", number, cdbun->ptr);
+       r = getuser(usbuf, cdbun->ptr);
+       cdb_free(cdbun);
+       return(r);
+}
+
+
+
+/*
+ * Helper function for rebuild_usersbynumber()
+ */
+void rebuild_ubn_for_user(struct ctdluser *usbuf, void *data) {
+
+       struct ubnlist {
+               struct ubnlist *next;
+               char username[USERNAME_SIZE];
+               long usernum;
+       };
+
+       static struct ubnlist *u = NULL;
+       struct ubnlist *ptr = NULL;
+
+       /* Lazy programming here.  Call this function as a ForEachUser backend
+        * in order to queue up the room names, or call it with a null user
+        * to make it do the processing.
+        */
+       if (usbuf != NULL) {
+               ptr = (struct ubnlist *) malloc(sizeof (struct ubnlist));
+               if (ptr == NULL) return;
+
+               ptr->usernum = usbuf->usernum;
+               safestrncpy(ptr->username, usbuf->fullname, sizeof ptr->username);
+               ptr->next = u;
+               u = ptr;
+               return;
+       }
+
+       while (u != NULL) {
+               CtdlLogPrintf(CTDL_DEBUG, "Rebuilding usersbynumber index %10ld : %s\n",
+                       u->usernum, u->username);
+               cdb_store(CDB_USERSBYNUMBER, &u->usernum, sizeof(long), u->username, strlen(u->username)+1);
+
+               ptr = u;
+               u = u->next;
+               free(ptr);
        }
-       return (-1);
 }
 
 
+
+/*
+ * Rebuild the users-by-number index
+ */
+void rebuild_usersbynumber(void) {
+       cdb_trunc(CDB_USERSBYNUMBER);                   /* delete the old indices */
+       ForEachUser(rebuild_ubn_for_user, NULL);        /* enumerate the users */
+       rebuild_ubn_for_user(NULL, NULL);               /* and index them */
+}
+
+
+
 /*
  * getuserbyuid()  -     get user by system uid (for PAM mode authentication)
  *                    returns 0 if user was found
@@ -421,6 +482,12 @@ int CtdlLoginExistingUser(char *authname, char *trythisname)
        }
 
        if (trythisname == NULL) return login_not_found;
+       
+       if (!strncasecmp(trythisname, "SYS_", 4))
+       {
+               CtdlLogPrintf(CTDL_DEBUG, "System user \"%s\" is not allowed to log in.\n", trythisname);
+               return login_not_found;
+       }
 
        /* If a "master user" is defined, handle its authentication if specified */
        CC->is_master = 0;
@@ -477,6 +544,34 @@ int CtdlLoginExistingUser(char *authname, char *trythisname)
 
        }
 
+#ifdef HAVE_LDAP
+       else if ((config.c_auth_mode == AUTHMODE_LDAP) || (config.c_auth_mode == AUTHMODE_LDAP_AD)) {
+       
+               /* LDAP auth mode */
+
+               uid_t ldap_uid;
+               char ldap_cn[256];
+               char ldap_dn[256];
+
+               found_user = CtdlTryUserLDAP(username, ldap_dn, sizeof ldap_dn, ldap_cn, sizeof ldap_cn, &ldap_uid);
+               if (found_user != 0) {
+                       return login_not_found;
+               }
+
+               found_user = getuserbyuid(&CC->user, ldap_uid);
+               if (found_user != 0) {
+                       create_user(trythisname, 0);
+                       found_user = getuserbyuid(&CC->user, ldap_uid);
+               }
+
+               if (found_user == 0) {
+                       if (CC->ldap_dn != NULL) free(CC->ldap_dn);
+                       CC->ldap_dn = strdup(ldap_dn);
+               }
+
+       }
+#endif
+
        else {
                /* native auth mode */
 
@@ -522,8 +617,11 @@ void cmd_user(char *cmdbuf)
        char username[256];
        int a;
 
+       CtdlLogPrintf(CTDL_DEBUG, "cmd_user(%s)\n", cmdbuf);
        extract_token(username, cmdbuf, 0, '|', sizeof username);
+       CtdlLogPrintf(CTDL_DEBUG, "username: %s\n", username);
        striplt(username);
+       CtdlLogPrintf(CTDL_DEBUG, "username: %s\n", username);
 
        a = CtdlLoginExistingUser(NULL, username);
        switch (a) {
@@ -554,8 +652,9 @@ void cmd_user(char *cmdbuf)
 /*
  * session startup code which is common to both cmd_pass() and cmd_newu()
  */
-void session_startup(void)
+void do_login(void)
 {
+       CC->logged_in = 1;
        CtdlLogPrintf(CTDL_NOTICE, "<%s> logged in\n", CC->curr_user);
 
        lgetuser(&CC->user, CC->curr_user);
@@ -596,6 +695,7 @@ void session_startup(void)
        create_room(MAILROOM, 4, "", 0, 1, 0, VIEW_MAILBOX);
        create_room(SENTITEMS, 4, "", 0, 1, 0, VIEW_MAILBOX);
        create_room(USERTRASHROOM, 4, "", 0, 1, 0, VIEW_MAILBOX);
+       /* create_room(USERDRAFTROOM, 4, "", 0, 1, 0, VIEW_MAILBOX); temporarily disabled for 7.60 */
 
        /* Run any startup routines registered by loadable modules */
        PerformSessionHooks(EVT_LOGIN);
@@ -659,13 +759,11 @@ void logout(void)
        CCC->logged_in = 0;
 
        /* Check to see if the user was deleted whilst logged in and purge them if necessary */
-       if (CCC->user.axlevel == 0)
+       if ((CCC->user.axlevel == 0) && (CCC->user.usernum))
                purge_user(CCC->user.fullname);
 
        /* Free any output buffers */
-       if (CCC->output_buffer != NULL) {
-               unbuffer_output();
-       }
+       unbuffer_output();
 }
 
 /*
@@ -674,6 +772,7 @@ void logout(void)
 static int validpw(uid_t uid, const char *pass)
 {
        char buf[256];
+       int rv = 0;
 
        if (IsEmptyStr(pass)) {
                CtdlLogPrintf(CTDL_DEBUG, "refusing to check empty password for uid=%d using chkpwd...\n", uid);
@@ -683,9 +782,9 @@ static int validpw(uid_t uid, const char *pass)
        CtdlLogPrintf(CTDL_DEBUG, "Validating password for uid=%d using chkpwd...\n", uid);
 
        begin_critical_section(S_CHKPWD);
-       write(chkpwd_write_pipe[1], &uid, sizeof(uid_t));
-       write(chkpwd_write_pipe[1], pass, 256);
-       read(chkpwd_read_pipe[0], buf, 4);
+       rv = write(chkpwd_write_pipe[1], &uid, sizeof(uid_t));
+       rv = write(chkpwd_write_pipe[1], pass, 256);
+       rv = read(chkpwd_read_pipe[0], buf, 4);
        end_critical_section(S_CHKPWD);
 
        if (!strncmp(buf, "PASS", 4)) {
@@ -742,13 +841,6 @@ void start_chkpwd_daemon(void) {
 }
 
 
-void do_login()
-{
-       (CC->logged_in) = 1;
-       session_startup();
-}
-
-
 int CtdlTryPassword(char *password)
 {
        int code;
@@ -804,6 +896,20 @@ int CtdlTryPassword(char *password)
                }
        }
 
+#ifdef HAVE_LDAP
+       else if ((config.c_auth_mode == AUTHMODE_LDAP) || (config.c_auth_mode == AUTHMODE_LDAP_AD)) {
+
+               /* LDAP auth mode */
+
+               if ((CC->ldap_dn) && (!CtdlTryPasswordLDAP(CC->ldap_dn, password))) {
+                       code = 0;
+               }
+               else {
+                       code = (-1);
+               }
+       }
+#endif
+
        else {
 
                /* native auth mode */
@@ -831,6 +937,7 @@ void cmd_pass(char *buf)
        char password[256];
        int a;
 
+       memset(password, 0, sizeof(password));
        extract_token(password, buf, 0, '|', sizeof password);
        a = CtdlTryPassword(password);
 
@@ -900,6 +1007,9 @@ int purge_user(char pname[])
        /* delete any existing user/room relationships */
        cdb_delete(CDB_VISIT, &usbuf.usernum, sizeof(long));
 
+       /* delete the users-by-number index record */
+       cdb_delete(CDB_USERSBYNUMBER, &usbuf.usernum, sizeof(long));
+
        /* delete the userlog entry */
        cdb_delete(CDB_USERS, usernamekey, strlen(usernamekey));
 
@@ -923,6 +1033,40 @@ int purge_user(char pname[])
 }
 
 
+int internal_create_user (char *username, struct ctdluser *usbuf, uid_t uid)
+{
+       if (!getuser(usbuf, username)) {
+               return (ERROR + ALREADY_EXISTS);
+       }
+
+       /* Go ahead and initialize a new user record */
+       memset(usbuf, 0, sizeof(struct ctdluser));
+       safestrncpy(usbuf->fullname, username, sizeof usbuf->fullname);
+       strcpy(usbuf->password, "");
+       usbuf->uid = uid;
+
+       /* These are the default flags on new accounts */
+       usbuf->flags = US_LASTOLD | US_DISAPPEAR | US_PAGINATOR | US_FLOORS;
+
+       usbuf->timescalled = 0;
+       usbuf->posted = 0;
+       usbuf->axlevel = config.c_initax;
+       usbuf->USscreenwidth = 80;
+       usbuf->USscreenheight = 24;
+       usbuf->lastcall = time(NULL);
+
+       /* fetch a new user number */
+       usbuf->usernum = get_new_user_number();
+
+       /* add user to the database */
+       putuser(usbuf);
+       cdb_store(CDB_USERSBYNUMBER, &usbuf->usernum, sizeof(long), usbuf->fullname, strlen(usbuf->fullname)+1);
+
+       return 0;
+}
+
+
+
 /*
  * create_user()  -  back end processing to create a new user
  *
@@ -937,11 +1081,14 @@ int create_user(char *newusername, int become_user)
        char username[256];
        char mailboxname[ROOMNAMELEN];
        char buf[SIZ];
+       int retval;
        uid_t uid = (-1);
+       
 
        safestrncpy(username, newusername, sizeof username);
        strproc(username);
 
+       
        if (config.c_auth_mode == AUTHMODE_HOST) {
 
                /* host auth mode */
@@ -964,14 +1111,7 @@ int create_user(char *newusername, int become_user)
                        uid = pd.pw_uid;
                        if (IsEmptyStr (username))
                        {
-                               CtdlLogPrintf (CTDL_EMERG, 
-                                        "Can't find Realname for user %s [%d] in the Host Auth Database; giving up.\n", 
-                                        newusername, pd.pw_uid);
-                               snprintf(buf, SIZ, 
-                                        "Can't find Realname for user %s [%d] in the Host Auth Database; giving up.\n",
-                                        newusername, pd.pw_uid);
-                               aide_message(buf, "User Creation Failure Notice");
-
+                               safestrncpy(username, pd.pw_name, sizeof username);
                        }
                }
                else {
@@ -979,37 +1119,17 @@ int create_user(char *newusername, int become_user)
                }
        }
 
-       if (!getuser(&usbuf, username)) {
-               return (ERROR + ALREADY_EXISTS);
-       }
-
-       /* Go ahead and initialize a new user record */
-       memset(&usbuf, 0, sizeof(struct ctdluser));
-       safestrncpy(usbuf.fullname, username, sizeof usbuf.fullname);
-       strcpy(usbuf.password, "");
-       usbuf.uid = uid;
-
-       /* These are the default flags on new accounts */
-       usbuf.flags = US_LASTOLD | US_DISAPPEAR | US_PAGINATOR | US_FLOORS;
-
-       usbuf.timescalled = 0;
-       usbuf.posted = 0;
-       usbuf.axlevel = config.c_initax;
-       usbuf.USscreenwidth = 80;
-       usbuf.USscreenheight = 24;
-       usbuf.lastcall = time(NULL);
-
-       /* fetch a new user number */
-       usbuf.usernum = get_new_user_number();
-
-       /* The very first user created on the system will always be an Aide */
-       if (usbuf.usernum == 1L) {
-               usbuf.axlevel = 6;
+#ifdef HAVE_LDAP
+       if ((config.c_auth_mode == AUTHMODE_LDAP) || (config.c_auth_mode == AUTHMODE_LDAP_AD)) {
+               if (CtdlTryUserLDAP(username, NULL, 0, username, sizeof username, &uid) != 0) {
+                       return(ERROR + NO_SUCH_USER);
+               }
        }
-
-       /* add user to userlog */
-       putuser(&usbuf);
-
+#endif /* HAVE_LDAP */
+       
+       if ((retval = internal_create_user(username, &usbuf, uid)) != 0)
+               return retval;
+       
        /*
         * Give the user a private mailbox and a configuration room.
         * Make the latter an invisible system room.
@@ -1035,7 +1155,7 @@ int create_user(char *newusername, int become_user)
                /* Now become the user we just created */
                memcpy(&CC->user, &usbuf, sizeof(struct ctdluser));
                safestrncpy(CC->curr_user, username, sizeof CC->curr_user);
-               CC->logged_in = 1;
+               do_login();
        
                /* Check to make sure we're still who we think we are */
                if (getuser(&CC->user, CC->curr_user)) {
@@ -1056,7 +1176,6 @@ int create_user(char *newusername, int become_user)
 
 
 
-
 /*
  * cmd_newu()  -  create a new user account and log in as that user
  */
@@ -1105,7 +1224,6 @@ void cmd_newu(char *cmdbuf)
        a = create_user(username, 1);
 
        if (a == 0) {
-               session_startup();
                logged_in_response();
        } else if (a == ERROR + ALREADY_EXISTS) {
                cprintf("%d '%s' already exists.\n",
@@ -1121,12 +1239,26 @@ void cmd_newu(char *cmdbuf)
 }
 
 
+/*
+ * set password - back end api code
+ */
+void CtdlSetPassword(char *new_pw)
+{
+       lgetuser(&CC->user, CC->curr_user);
+       safestrncpy(CC->user.password, new_pw, sizeof(CC->user.password));
+       lputuser(&CC->user);
+       CtdlLogPrintf(CTDL_INFO, "Password changed for user <%s>\n", CC->curr_user);
+       PerformSessionHooks(EVT_SETPASS);
+}
+
 
 /*
- * set password
+ * set password - citadel protocol implementation
  */
 void cmd_setp(char *new_pw)
 {
+       int generate_random_password = 0;
+
        if (CtdlAccessCheck(ac_logged_in)) {
                return;
        }
@@ -1139,17 +1271,23 @@ void cmd_setp(char *new_pw)
                        ERROR + NOT_HERE);
                return;
        }
-       strproc(new_pw);
-       if (IsEmptyStr(new_pw)) {
-               cprintf("%d Password unchanged.\n", CIT_OK);
-               return;
+
+       if (!strcasecmp(new_pw, "GENERATE_RANDOM_PASSWORD")) {
+               char random_password[17];
+               generate_random_password = 1;
+               snprintf(random_password, sizeof random_password, "%08lx%08lx", random(), random());
+               CtdlSetPassword(random_password);
+               cprintf("%d %s\n", CIT_OK, random_password);
+       }
+       else {
+               strproc(new_pw);
+               if (IsEmptyStr(new_pw)) {
+                       cprintf("%d Password unchanged.\n", CIT_OK);
+                       return;
+               }
+               CtdlSetPassword(new_pw);
+               cprintf("%d Password changed.\n", CIT_OK);
        }
-       lgetuser(&CC->user, CC->curr_user);
-       safestrncpy(CC->user.password, new_pw, sizeof(CC->user.password));
-       lputuser(&CC->user);
-       cprintf("%d Password changed.\n", CIT_OK);
-       CtdlLogPrintf(CTDL_INFO, "Password changed for user <%s>\n", CC->curr_user);
-       PerformSessionHooks(EVT_SETPASS);
 }
 
 
@@ -1208,7 +1346,7 @@ void cmd_creu(char *cmdbuf)
 /*
  * get user parameters
  */
-void cmd_getu(void)
+void cmd_getu(char *cmdbuf)
 {
 
        if (CtdlAccessCheck(ac_logged_in))
@@ -1393,6 +1531,8 @@ void cmd_invt_kick(char *iuser, int op) {
        return;
 }
 
+void cmd_invt(char *iuser) {cmd_invt_kick(iuser, 1);}
+void cmd_kick(char *iuser) {cmd_invt_kick(iuser, 0);}
 
 /*
  * Forget (Zap) the current room (API call)
@@ -1426,7 +1566,7 @@ int CtdlForgetThisRoom(void) {
 /*
  * forget (Zap) the current room
  */
-void cmd_forg(void)
+void cmd_forg(char *argbuf)
 {
 
        if (CtdlAccessCheck(ac_logged_in)) {
@@ -1444,7 +1584,7 @@ void cmd_forg(void)
 /*
  * Get Next Unregistered User
  */
-void cmd_gnur(void)
+void cmd_gnur(char *argbuf)
 {
        struct cdbdata *cdbus;
        struct ctdluser usbuf;
@@ -1567,16 +1707,13 @@ void ListThisUser(struct ctdluser *usbuf, void *data)
                if ((CC->user.axlevel >= 6)
                    || ((usbuf->flags & US_UNLISTED) == 0)
                    || ((CC->internal_pgm))) {
-                       cprintf("%s|%d|%ld|%ld|%ld|%ld|",
+                       cprintf("%s|%d|%ld|%ld|%ld|%ld||\n",
                                usbuf->fullname,
                                usbuf->axlevel,
                                usbuf->usernum,
                                (long)usbuf->lastcall,
                                usbuf->timescalled,
                                usbuf->posted);
-                       if (CC->user.axlevel >= 6)
-                               cprintf("%s", usbuf->password);
-                       cprintf("\n");
                }
        }
 }
@@ -1600,7 +1737,7 @@ void cmd_list(char *cmdbuf)
 /*
  * assorted info we need to check at login
  */
-void cmd_chek(void)
+void cmd_chek(char *argbuf)
 {
        int mail = 0;
        int regis = 0;
@@ -1870,3 +2007,38 @@ void cmd_renu(char *cmdbuf)
 
        cprintf("%d An unknown error occurred.\n", ERROR);
 }
+
+
+
+/*****************************************************************************/
+/*                      MODULE INITIALIZATION STUFF                          */
+/*****************************************************************************/
+
+
+CTDL_MODULE_INIT(user_ops)
+{
+       CtdlRegisterProtoHook(cmd_user, "USER", "Autoconverted. TODO: document me.");
+       CtdlRegisterProtoHook(cmd_pass, "PASS", "Autoconverted. TODO: document me.");
+       CtdlRegisterProtoHook(cmd_creu, "CREU", "Autoconverted. TODO: document me.");
+       CtdlRegisterProtoHook(cmd_setp, "SETP", "Autoconverted. TODO: document me.");
+       CtdlRegisterProtoHook(cmd_getu, "GETU", "Autoconverted. TODO: document me.");
+       CtdlRegisterProtoHook(cmd_setu, "SETU", "Autoconverted. TODO: document me.");
+       CtdlRegisterProtoHook(cmd_slrp, "SLRP", "Autoconverted. TODO: document me.");
+       CtdlRegisterProtoHook(cmd_invt, "INVT", "Autoconverted. TODO: document me.");
+       CtdlRegisterProtoHook(cmd_kick, "KICK", "Autoconverted. TODO: document me.");
+       CtdlRegisterProtoHook(cmd_forg, "FORG", "Autoconverted. TODO: document me.");
+       CtdlRegisterProtoHook(cmd_gnur, "GNUR", "Autoconverted. TODO: document me.");
+       CtdlRegisterProtoHook(cmd_vali, "VALI", "Autoconverted. TODO: document me.");
+       CtdlRegisterProtoHook(cmd_list, "LIST", "Autoconverted. TODO: document me.");
+       CtdlRegisterProtoHook(cmd_chek, "CHEK", "Autoconverted. TODO: document me.");
+       CtdlRegisterProtoHook(cmd_qusr, "QUSR", "Autoconverted. TODO: document me.");
+       CtdlRegisterProtoHook(cmd_agup, "AGUP", "Autoconverted. TODO: document me.");
+       CtdlRegisterProtoHook(cmd_asup, "ASUP", "Autoconverted. TODO: document me.");
+       CtdlRegisterProtoHook(cmd_seen, "SEEN", "Autoconverted. TODO: document me.");
+       CtdlRegisterProtoHook(cmd_gtsn, "GTSN", "Autoconverted. TODO: document me.");
+       CtdlRegisterProtoHook(cmd_view, "VIEW", "Autoconverted. TODO: document me.");
+       CtdlRegisterProtoHook(cmd_renu, "RENU", "Autoconverted. TODO: document me.");
+       CtdlRegisterProtoHook(cmd_newu, "NEWU", "Autoconverted. TODO: document me.");
+       /* return our Subversion id for the Log */
+       return "$Id$";
+}