X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fuser_ops.c;h=0dc5ae3276b4909587f673041368b5f53cc2fc8a;hb=882ff5a53c3b4e440520a073cf07dc60b2671876;hp=9b4ee0d4f42666270662c6535783fc074f71455b;hpb=df8fe1be788cd4193b9e213009b665b47f1ed385;p=citadel.git diff --git a/citadel/user_ops.c b/citadel/user_ops.c index 9b4ee0d4f..0dc5ae327 100644 --- a/citadel/user_ops.c +++ b/citadel/user_ops.c @@ -1,7 +1,7 @@ /* * Server functions which perform operations on user objects. * - * Copyright (c) 1987-2017 by the citadel.org team + * Copyright (c) 1987-2020 by the citadel.org team * * This program is open source software; you can redistribute it and/or * modify it under the terms of the GNU General Public License, version 3. @@ -32,6 +32,44 @@ int chkpwd_write_pipe[2]; int chkpwd_read_pipe[2]; +/* + * makeuserkey() - convert a username into the format used as a database key + * "key" must be a buffer of at least USERNAME_SIZE + * (Key format is the username with all non-alphanumeric characters removed, and converted to lower case.) + */ +void makeuserkey(char *key, const char *username) { + int i; + int keylen = 0; + + if (IsEmptyStr(username)) { + key[0] = 0; + return; + } + + int len = strlen(username); + for (i=0; ((i<=len) && (ifullname, cutuserkey(usbuf->fullname)); + makeuserkey(usernamekey, usbuf->fullname); usbuf->version = REV_LEVEL; cdb_store(CDB_USERS, usernamekey, strlen(usernamekey), usbuf, sizeof(struct ctdluser)); } @@ -125,8 +162,8 @@ int rename_user(char *oldname, char *newname) { char newnamekey[USERNAME_SIZE]; /* Create the database keys... */ - makeuserkey(oldnamekey, oldname, cutuserkey(oldname)); - makeuserkey(newnamekey, newname, cutuserkey(newname)); + makeuserkey(oldnamekey, oldname); + makeuserkey(newnamekey, newname); /* Lock up and get going */ begin_critical_section(S_USERS); @@ -145,10 +182,8 @@ int rename_user(char *oldname, char *newname) { if (CtdlGetUser(&usbuf, oldname) != 0) { retcode = RENAMEUSER_NOT_FOUND; } - else { /* Sanity checks succeeded. Now rename the user. */ - if (usbuf.usernum == 0) - { + if (usbuf.usernum == 0) { syslog(LOG_DEBUG, "user_ops: can not rename user \"Citadel\"."); retcode = RENAMEUSER_NOT_FOUND; } else { @@ -168,6 +203,58 @@ int rename_user(char *oldname, char *newname) { } +/* + * Convert a username into the format used as a database key prior to version 928 + * This only gets called by reindex_user_928() + */ +void makeuserkey_pre928(char *key, const char *username) { + int i; + + int len = strlen(username); + + if (len >= USERNAME_SIZE) { + syslog(LOG_INFO, "Username too long: %s", username); + len = USERNAME_SIZE - 1; + } + for (i=0; i<=len; ++i) { + key[i] = tolower(username[i]); + } +} + + +/* + * Read a user record using the pre-v928 index format, and write it back using the v928-and-higher index format. + * This ONLY gets called during an upgrade from version <928 to version >=928. + */ +void reindex_user_928(char *username, void *out_data) { + + char oldkey[USERNAME_SIZE]; + char newkey[USERNAME_SIZE]; + struct cdbdata *cdbus; + struct ctdluser usbuf; + + makeuserkey_pre928(oldkey, username); + makeuserkey(newkey, username); + + syslog(LOG_DEBUG, "user_ops: reindex_user_928: %s <%s> --> <%s>", username, oldkey, newkey); + + // Fetch the user record using the old index format + cdbus = cdb_fetch(CDB_USERS, oldkey, strlen(oldkey)); + if (cdbus == NULL) { + syslog(LOG_INFO, "user_ops: <%s> not found, were they already reindexed?", username); + return; + } + memcpy(&usbuf, cdbus->ptr, ((cdbus->len > sizeof(struct ctdluser)) ? sizeof(struct ctdluser) : cdbus->len)); + cdb_free(cdbus); + + // delete the old record + cdb_delete(CDB_USERS, oldkey, strlen(oldkey)); + + // write the new record + cdb_store(CDB_USERS, newkey, strlen(newkey), &usbuf, sizeof(struct ctdluser)); +} + + /* * Index-generating function used by Ctdl[Get|Set]Relationship */ @@ -176,7 +263,6 @@ int GenerateRelationshipIndex(char *IndexBuf, long RoomGen, long UserID) { - struct { long iRoomID; long iRoomGen; @@ -214,10 +300,7 @@ void put_visit(visit *newvisit) /* * Define a relationship between a user and a room */ -void CtdlSetRelationship(visit *newvisit, - struct ctdluser *rel_user, - struct ctdlroom *rel_room) -{ +void CtdlSetRelationship(visit *newvisit, struct ctdluser *rel_user, struct ctdlroom *rel_room) { /* We don't use these in Citadel because they're implicit by the * index, but they must be present if the database is exported. */ @@ -232,10 +315,7 @@ void CtdlSetRelationship(visit *newvisit, /* * Locate a relationship between a user and a room */ -void CtdlGetRelationship(visit *vbuf, - struct ctdluser *rel_user, - struct ctdlroom *rel_room) -{ +void CtdlGetRelationship(visit *vbuf, struct ctdluser *rel_user, struct ctdlroom *rel_room) { char IndexBuf[32]; int IndexLen; struct cdbdata *cdbvisit; @@ -397,38 +477,12 @@ int CtdlGetUserByNumber(struct ctdluser *usbuf, long number) /* * 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; +void rebuild_ubn_for_user(char *username, void *data) { + struct ctdluser u; - ptr->usernum = usbuf->usernum; - safestrncpy(ptr->username, usbuf->fullname, sizeof ptr->username); - ptr->next = u; - u = ptr; - return; - } - - while (u != NULL) { - syslog(LOG_DEBUG, "user_ops: rebuilding usersbynumber index %10ld : %s", 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); + syslog(LOG_DEBUG, "user_ops: rebuilding usersbynumber index for %s", username); + if (CtdlGetUser(&u, username) == 0) { + cdb_store(CDB_USERSBYNUMBER, &(u.usernum), sizeof(long), u.fullname, strlen(u.fullname)+1); } } @@ -439,7 +493,6 @@ void rebuild_ubn_for_user(struct ctdluser *usbuf, void *data) { 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 */ } @@ -475,15 +528,13 @@ int getuserbyuid(struct ctdluser *usbuf, uid_t number) /* * Back end for cmd_user() and its ilk - * - * NOTE: "authname" should only be used if we are attempting to use the "master user" feature */ -int CtdlLoginExistingUser(char *authname, const char *trythisname) +int CtdlLoginExistingUser(const char *trythisname) { char username[SIZ]; int found_user; - syslog(LOG_DEBUG, "user_ops: CtdlLoginExistingUser(%s, %s)", authname, trythisname); + syslog(LOG_DEBUG, "user_ops: CtdlLoginExistingUser(%s)", trythisname); if ((CC->logged_in)) { return login_already_logged_in; @@ -497,16 +548,6 @@ int CtdlLoginExistingUser(char *authname, const char *trythisname) return login_not_found; } - /* If a "master user" is defined, handle its authentication if specified */ - CC->is_master = 0; - if ( (!IsEmptyStr(CtdlGetConfigStr("c_master_user"))) && - (!IsEmptyStr(CtdlGetConfigStr("c_master_pass"))) && - (authname != NULL) && - (!strcasecmp(authname, CtdlGetConfigStr("c_master_user"))) ) - { - CC->is_master = 1; - } - /* Continue attempting user validation... */ safestrncpy(username, trythisname, sizeof (username)); striplt(username); @@ -541,9 +582,7 @@ int CtdlLoginExistingUser(char *authname, const char *trythisname) return login_not_found; } - /* Locate the associated Citadel account. - * If not found, make one attempt to create it. - */ + /* Locate the associated Citadel account. If not found, make one attempt to create it. */ found_user = getuserbyuid(&CC->user, pd.pw_uid); if (found_user != 0) { create_user(username, CREATE_USER_DO_NOT_BECOME_USER, pd.pw_uid); @@ -584,7 +623,7 @@ int CtdlLoginExistingUser(char *authname, const char *trythisname) else { /* native auth mode */ - recptypes *valid = NULL; + struct recptypes *valid = NULL; /* First, try to log in as if the supplied name is a display name */ found_user = CtdlGetUser(&CC->user, username); @@ -634,9 +673,7 @@ void do_login(void) CC->user.axlevel = AxAideU; } - /* If we're authenticating off the host system, automatically give - * root the highest level of access. - */ + /* If we're authenticating off the host system, automatically give root the highest level of access. */ if (CtdlGetConfigInt("c_auth_mode") == AUTHMODE_HOST) { if (CC->user.uid == 0) { CC->user.axlevel = AxAideU; @@ -644,9 +681,7 @@ void do_login(void) } CtdlPutUserLock(&CC->user); - /* - * If we are using LDAP authentication, extract the user's email addresses from the directory. - */ + /* If we are using LDAP authentication, extract the user's email addresses from the directory. */ #ifdef HAVE_LDAP if ((CtdlGetConfigInt("c_auth_mode") == AUTHMODE_LDAP) || (CtdlGetConfigInt("c_auth_mode") == AUTHMODE_LDAP_AD)) { char new_emailaddrs[512]; @@ -658,13 +693,16 @@ void do_login(void) } #endif - /* - * No email address for user? Make one up. (commented out because it appears to break things) + /* If the user does not have any email addresses assigned, generate one. */ if (IsEmptyStr(CC->user.emailaddrs)) { - sprintf(CC->user.emailaddrs, "cit%ld@%s", CC->user.usernum, CtdlGetConfigStr("c_fqdn")); + AutoGenerateEmailAddressForUser(&CC->user); } - */ - + + /* Populate the user principal identity, which is consistent and never aliased */ + strcpy(CC->cs_principal_id, ""); + makeuserkey(CC->cs_principal_id, CC->user.fullname); + strcat(CC->cs_principal_id, "@"); + strcat(CC->cs_principal_id, CtdlGetConfigStr("c_fqdn")); /* * Populate cs_inet_email and cs_inet_other_emails with valid email addresses from the user record @@ -721,12 +759,9 @@ void CtdlUserLogout(void) * since it's possible to log in again without reconnecting, we cannot * make that assumption. */ - strcpy(CCC->fake_username, ""); - strcpy(CCC->fake_hostname, ""); - strcpy(CCC->fake_roomname, ""); CCC->logged_in = 0; - /* Check to see if the user was deleted whilst logged in and purge them if necessary */ + /* Check to see if the user was deleted while logged in and purge them if necessary */ if ((CCC->user.axlevel == AxDeleted) && (CCC->user.usernum)) { purge_user(CCC->user.fullname); } @@ -734,13 +769,9 @@ void CtdlUserLogout(void) /* Clear out the user record in memory so we don't behave like a ghost */ memset(&CCC->user, 0, sizeof(struct ctdluser)); CCC->curr_user[0] = 0; - CCC->is_master = 0; CCC->cs_inet_email[0] = 0; CCC->cs_inet_other_emails[0] = 0; CCC->cs_inet_fn[0] = 0; - CCC->fake_username[0] = 0; - CCC->fake_hostname[0] = 0; - CCC->fake_roomname[0] = 0; /* Free any output buffers */ unbuffer_output(); @@ -855,10 +886,6 @@ int CtdlTryPassword(const char *password, long len) return pass_wrong_password; } - if (CCC->is_master) { - code = strcmp(password, CtdlGetConfigStr("c_master_pass")); - } - else if (CtdlGetConfigInt("c_auth_mode") == AUTHMODE_HOST) { /* host auth mode */ @@ -944,11 +971,12 @@ int purge_user(char pname[]) struct ctdluser usbuf; char usernamekey[USERNAME_SIZE]; - makeuserkey(usernamekey, pname, cutuserkey(pname)); + makeuserkey(usernamekey, pname); /* If the name is empty we can't find them in the DB any way so just return */ - if (IsEmptyStr(pname)) + if (IsEmptyStr(pname)) { return(ERROR + NO_SUCH_USER); + } if (CtdlGetUser(&usbuf, pname) != 0) { syslog(LOG_ERR, "user_ops: cannot purge user <%s> - not found", pname); @@ -1169,51 +1197,50 @@ int CtdlForgetThisRoom(void) { /* - * Traverse the user file... + * Traverse the user file and perform a callback for each user record. + * (New improved version that runs in two phases so that callbacks can perform writes without having a r/o cursor open) */ -void ForEachUser(void (*CallBack) (struct ctdluser * EachUser, void *out_data), - void *in_data) +void ForEachUser(void (*CallBack) (char *, void *out_data), void *in_data) { - struct ctdluser usbuf; struct cdbdata *cdbus; + struct ctdluser *usptr; + + struct feu { + struct feu *next; + char username[USERNAME_SIZE]; + }; + struct feu *ufirst = NULL; + struct feu *ulast = NULL; + struct feu *f = NULL; cdb_rewind(CDB_USERS); + // Phase 1 : build a linked list of all our user account names 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); - (*CallBack) (&usbuf, in_data); - } -} - + usptr = (struct ctdluser *) cdbus->ptr; -/* - * List one user (this works with cmd_list) - */ -void ListThisUser(struct ctdluser *usbuf, void *data) -{ - char *searchstring; + if (strlen(usptr->fullname) > 0) { + f = malloc(sizeof(struct feu)); + f->next = NULL; + strncpy(f->username, usptr->fullname, USERNAME_SIZE); - searchstring = (char *)data; - if (bmstrcasestr(usbuf->fullname, searchstring) == NULL) { - return; + if (ufirst == NULL) { + ufirst = f; + ulast = f; + } + else { + ulast->next = f; + ulast = f; + } + } } - if (usbuf->axlevel > AxDeleted) { - if ((CC->user.axlevel >= AxAideU) - || ((usbuf->flags & US_UNLISTED) == 0) - || ((CC->internal_pgm))) { - cprintf("%s|%d|%ld|%ld|%ld|%ld||\n", - usbuf->fullname, - usbuf->axlevel, - usbuf->usernum, - (long)usbuf->lastcall, - usbuf->timescalled, - usbuf->posted); - } + // Phase 2 : perform the callback for each user while de-allocating the list + while (ufirst != NULL) { + (*CallBack) (ufirst->username, in_data); + f = ufirst; + ufirst = ufirst->next; + free(f); } }