From: Art Cancro Date: Thu, 9 Dec 1999 05:01:15 +0000 (+0000) Subject: * Split cmd_user() and cmd_pass() into frontend/backend functions X-Git-Tag: v7.86~7397 X-Git-Url: https://code.citadel.org/?p=citadel.git;a=commitdiff_plain;h=be52a0c1ccd622a23fdb37076251b3df1bb99ae0 * Split cmd_user() and cmd_pass() into frontend/backend functions * serv_smtp: implemented AUTH LOGIN for client authentication --- diff --git a/citadel/ChangeLog b/citadel/ChangeLog index d9d64edec..e5961c534 100644 --- a/citadel/ChangeLog +++ b/citadel/ChangeLog @@ -1,4 +1,8 @@ $Log$ +Revision 1.426 1999/12/09 05:01:14 ajc +* Split cmd_user() and cmd_pass() into frontend/backend functions +* serv_smtp: implemented AUTH LOGIN for client authentication + Revision 1.425 1999/12/09 00:22:58 ajc * Finished the "arbitrary service" registration. * Eliminated "special" master socket for Citadel protocol - just register it @@ -1482,3 +1486,4 @@ Sat Jul 11 00:20:48 EDT 1998 Nathan Bryant Fri Jul 10 1998 Art Cancro * Initial CVS import + diff --git a/citadel/citserver.c b/citadel/citserver.c index 7b8cc1af8..e9573ad58 100644 --- a/citadel/citserver.c +++ b/citadel/citserver.c @@ -243,6 +243,7 @@ void CtdlAllocUserData(unsigned long requested_sym, size_t num_bytes) ptr = mallok(sizeof(struct CtdlSessData)); ptr->sym_id = requested_sym; ptr->sym_data = mallok(num_bytes); + memset(ptr->sym_data, 0, num_bytes); begin_critical_section(S_SESSION_TABLE); ptr->next = CC->FirstSessData; @@ -866,7 +867,7 @@ void begin_session(struct CitContext *con) time(&con->lastidle); strcpy(con->lastcmdname, " "); strcpy(con->cs_clientname, "(unknown)"); - strcpy(con->curr_user,"(not logged in)"); + strcpy(con->curr_user, NLI); strcpy(con->net_node,""); snprintf(con->temp, sizeof con->temp, tmpnam(NULL)); safestrncpy(con->cs_host, config.c_fqdn, sizeof con->cs_host); diff --git a/citadel/serv_smtp.c b/citadel/serv_smtp.c index 87aef1acb..6d0317805 100644 --- a/citadel/serv_smtp.c +++ b/citadel/serv_smtp.c @@ -21,9 +21,25 @@ #include "config.h" #include "dynloader.h" #include "room_ops.h" +#include "user_ops.h" #include "policy.h" #include "database.h" #include "msgbase.h" +#include "tools.h" + +struct citsmtp { + int command_state; +}; + +enum { + smtp_command, + smtp_user, + smtp_password +}; + +#define SMTP ((struct citsmtp *)CtdlGetUserData(SYM_SMTP)) + +long SYM_SMTP; /* @@ -32,12 +48,107 @@ void smtp_greeting(void) { strcpy(CC->cs_clientname, "Citadel SMTP"); + CtdlAllocUserData(SYM_SMTP, sizeof(struct citsmtp)); - cprintf("220 %s Citadel/UX SMTP server ready\n", + cprintf("220 Welcome to the Citadel/UX ESMTP server at %s\n", config.c_fqdn); } +/* + * Implement HELO and EHLO commands. + */ +void smtp_hello(char *argbuf, int is_esmtp) { + + if (!is_esmtp) { + cprintf("250 Greetings and joyous salutations.\n"); + } + else { + cprintf("250-Greetings and joyous salutations.\n"); + cprintf("250-HELP\n"); + cprintf("250-SIZE %ld\n", config.c_maxmsglen); + cprintf("250 AUTH=LOGIN\n"); + } +} + + +/* + * Implement HELP command. + */ +void smtp_help(void) { + cprintf("214-Here's the frequency, Kenneth:\n"); + cprintf("214- EHLO\n"); + cprintf("214- HELO\n"); + cprintf("214- HELP\n"); + cprintf("214- NOOP\n"); + cprintf("214- QUIT\n"); + cprintf("214 I'd tell you more, but then I'd have to kill you.\n"); +} + + +/* + * + */ +void smtp_get_user(char *argbuf) { + char buf[256]; + char username[256]; + + decode_base64(username, argbuf); + lprintf(9, "Trying <%s>\n", username); + if (CtdlLoginExistingUser(username) == login_ok) { + encode_base64(buf, "Password:"); + cprintf("334 %s\n", buf); + SMTP->command_state = smtp_password; + } + else { + cprintf("500 No such user.\n"); + SMTP->command_state = smtp_command; + } +} + + +/* + * + */ +void smtp_get_pass(char *argbuf) { + char password[256]; + + decode_base64(password, argbuf); + lprintf(9, "Trying <%s>\n", password); + if (CtdlTryPassword(password) == pass_ok) { + cprintf("235 Authentication successful.\n"); + lprintf(9, "SMTP auth login successful\n"); + } + else { + cprintf("500 Authentication failed.\n"); + } + SMTP->command_state = smtp_command; +} + + +/* + * + */ +void smtp_auth(char *argbuf) { + char buf[256]; + + if (strncasecmp(argbuf, "login", 5) ) { + cprintf("500 We only support LOGIN authentication.\n"); + return; + } + + if (strlen(argbuf) >= 7) { + smtp_get_user(&argbuf[6]); + } + + else { + encode_base64(buf, "Username:"); + cprintf("334 %s\n", buf); + SMTP->command_state = smtp_user; + } +} + + /* * Main command loop for SMTP sessions. */ @@ -52,9 +163,38 @@ void smtp_command_loop(void) { return; } lprintf(5, "citserver[%3d]: %s\n", CC->cs_pid, cmdbuf); + while (strlen(cmdbuf) < 5) strcat(cmdbuf, " "); + + if (SMTP->command_state == smtp_user) { + smtp_get_user(cmdbuf); + } + + else if (SMTP->command_state == smtp_password) { + smtp_get_pass(cmdbuf); + } + + else if (!strncasecmp(cmdbuf, "AUTH", 4)) { + smtp_auth(&cmdbuf[5]); + } + + else if (!strncasecmp(cmdbuf, "EHLO", 4)) { + smtp_hello(&cmdbuf[5], 1); + } + + else if (!strncasecmp(cmdbuf, "HELO", 4)) { + smtp_hello(&cmdbuf[5], 0); + } + + else if (!strncasecmp(cmdbuf, "HELP", 4)) { + smtp_help(); + } + + else if (!strncasecmp(cmdbuf, "NOOP", 4)) { + cprintf("250 This command successfully did nothing.\n"); + } - if (!strncasecmp(cmdbuf,"QUIT",4)) { - cprintf("221 Later, dude! Microsoft sucks!!\n"); + else if (!strncasecmp(cmdbuf, "QUIT", 4)) { + cprintf("221 Goodbye...\n"); CC->kill_me = 1; return; } @@ -69,7 +209,8 @@ void smtp_command_loop(void) { char *Dynamic_Module_Init(void) { - CtdlRegisterServiceHook(2525, + SYM_SMTP = CtdlGetDynamicSymbol(); + CtdlRegisterServiceHook(25, smtp_greeting, smtp_command_loop); return "$Id$"; diff --git a/citadel/sysconfig.h b/citadel/sysconfig.h index 10796413d..a32afa728 100644 --- a/citadel/sysconfig.h +++ b/citadel/sysconfig.h @@ -68,6 +68,9 @@ #define HOUSEKEEPING_WAKEUP 60 +#define NLI "(not logged in)" + + /*** STRUCTURE SIZE VARIABLES ***/ /* You may NOT change this value once you set up your system. */ diff --git a/citadel/sysdep.c b/citadel/sysdep.c index b5ad3bc44..63b89bebf 100644 --- a/citadel/sysdep.c +++ b/citadel/sysdep.c @@ -763,37 +763,16 @@ int main(int argc, char **argv) get_config(); /* - * Bind the server to our favourite port. - */ - CtdlRegisterServiceHook(config.c_port_number, - citproto_begin_session, - do_command_loop); - - /* - * Now that we've bound the socket, change to the BBS user id and its - * corresponding group ids + * Do non system dependent startup functions. */ - if (drop_root_perms) { - if ((pw = getpwuid(BBSUID)) == NULL) - lprintf(1, "WARNING: getpwuid(%d): %s\n" - "Group IDs will be incorrect.\n", BBSUID, - strerror(errno)); - else { - initgroups(pw->pw_name, pw->pw_gid); - if (setgid(pw->pw_gid)) - lprintf(3, "setgid(%d): %s\n", pw->pw_gid, - strerror(errno)); - } - lprintf(7, "Changing uid to %d\n", BBSUID); - if (setuid(BBSUID) != 0) { - lprintf(3, "setuid() failed: %s\n", strerror(errno)); - } - } + master_startup(); /* - * Do non system dependent startup functions. + * Bind the server to our favorite ports. */ - master_startup(); + CtdlRegisterServiceHook(config.c_port_number, + citproto_begin_session, + do_command_loop); /* * Load any server-side modules (plugins) available here. @@ -857,6 +836,27 @@ int main(int argc, char **argv) } + /* + * Now that we've bound the sockets, change to the BBS user id and its + * corresponding group ids + */ + if (drop_root_perms) { + if ((pw = getpwuid(BBSUID)) == NULL) + lprintf(1, "WARNING: getpwuid(%d): %s\n" + "Group IDs will be incorrect.\n", BBSUID, + strerror(errno)); + else { + initgroups(pw->pw_name, pw->pw_gid); + if (setgid(pw->pw_gid)) + lprintf(3, "setgid(%d): %s\n", pw->pw_gid, + strerror(errno)); + } + lprintf(7, "Changing uid to %d\n", BBSUID); + if (setuid(BBSUID) != 0) { + lprintf(3, "setuid() failed: %s\n", strerror(errno)); + } + } + /* * Now create a bunch of worker threads. */ diff --git a/citadel/tools.c b/citadel/tools.c index 0b7e1aaf7..0a87ebd5f 100644 --- a/citadel/tools.c +++ b/citadel/tools.c @@ -8,6 +8,13 @@ #include #include "tools.h" +#define TRUE 1 +#define FALSE 0 + +typedef unsigned char byte; /* Byte type */ +static byte dtable[256]; /* base64 encode / decode table */ + + char *safestrncpy(char *dest, const char *src, size_t n) { if (dest == NULL || src == NULL) @@ -83,3 +90,127 @@ long extract_long(char *source, long int parmnum) extract_token(buf, source, parmnum, '|'); return(atol(buf)); } + + + +/* + * decode_base64() and encode_base64() are adaptations of code by + * John Walker, found in full in the file "base64.c" included with this + * distribution. The difference between those functions and these is that + * these are intended to encode/decode small string buffers, and those are + * intended to encode/decode entire MIME parts. + */ + +void encode_base64(char *dest, char *source) +{ + int i, hiteof = FALSE; + int spos = 0; + int dpos = 0; + + /* Fill dtable with character encodings. */ + + for (i = 0; i < 26; i++) { + dtable[i] = 'A' + i; + dtable[26 + i] = 'a' + i; + } + for (i = 0; i < 10; i++) { + dtable[52 + i] = '0' + i; + } + dtable[62] = '+'; + dtable[63] = '/'; + + while (!hiteof) { + byte igroup[3], ogroup[4]; + int c, n; + + igroup[0] = igroup[1] = igroup[2] = 0; + for (n = 0; n < 3; n++) { + c = source[spos++]; + if (c == 0) { + hiteof = TRUE; + break; + } + igroup[n] = (byte) c; + } + if (n > 0) { + ogroup[0] = dtable[igroup[0] >> 2]; + ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)]; + ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)]; + ogroup[3] = dtable[igroup[2] & 0x3F]; + + /* Replace characters in output stream with "=" pad + characters if fewer than three characters were + read from the end of the input stream. */ + + if (n < 3) { + ogroup[3] = '='; + if (n < 2) { + ogroup[2] = '='; + } + } + for (i = 0; i < 4; i++) { + dest[dpos++] = ogroup[i]; + dest[dpos] = 0; + } + } + } +} + + + +void decode_base64(char *dest, char *source) +{ + int i; + int dpos = 0; + int spos = 0; + + for (i = 0; i < 255; i++) { + dtable[i] = 0x80; + } + for (i = 'A'; i <= 'Z'; i++) { + dtable[i] = 0 + (i - 'A'); + } + for (i = 'a'; i <= 'z'; i++) { + dtable[i] = 26 + (i - 'a'); + } + for (i = '0'; i <= '9'; i++) { + dtable[i] = 52 + (i - '0'); + } + dtable['+'] = 62; + dtable['/'] = 63; + dtable['='] = 0; + + /*CONSTANTCONDITION*/ + while (TRUE) { + byte a[4], b[4], o[3]; + + for (i = 0; i < 4; i++) { + int c = source[spos++]; + + if (c == 0) { + if (i > 0) { + return; + } + return; + } + if (dtable[c] & 0x80) { + /* Ignoring errors: discard invalid character. */ + i--; + continue; + } + a[i] = (byte) c; + b[i] = (byte) dtable[c]; + } + o[0] = (b[0] << 2) | (b[1] >> 4); + o[1] = (b[1] << 4) | (b[2] >> 2); + o[2] = (b[2] << 6) | b[3]; + i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3); + if (i>=1) dest[dpos++] = o[0]; + if (i>=2) dest[dpos++] = o[1]; + if (i>=3) dest[dpos++] = o[2]; + dest[dpos] = 0; + if (i < 3) { + return; + } + } +} diff --git a/citadel/tools.h b/citadel/tools.h index 7024f3117..a2ae48098 100644 --- a/citadel/tools.h +++ b/citadel/tools.h @@ -4,5 +4,8 @@ int num_parms (char *source); void extract_token(char *dest, char *source, int parmnum, char separator); int extract_int (char *source, int parmnum); long int extract_long (char *source, long int parmnum); +void encode_base64(char *dest, char *source); +void decode_base64(char *dest, char *source); + #define extract(dest,source,parmnum) extract_token(dest,source,parmnum,'|') diff --git a/citadel/user_ops.c b/citadel/user_ops.c index dc83df311..3ed2d2868 100644 --- a/citadel/user_ops.c +++ b/citadel/user_ops.c @@ -242,24 +242,21 @@ int getuserbynumber(struct usersupp *usbuf, long int number) /* - * USER cmd + * Back end for cmd_user() and its ilk */ -void cmd_user(char *cmdbuf) +int CtdlLoginExistingUser(char *username) { - char username[256]; char autoname[256]; int found_user = 0; struct passwd *p; int a; - extract(username,cmdbuf,0); username[25] = 0; strproc(username); if ((CC->logged_in)) { - cprintf("%d Already logged in.\n",ERROR); - return; - } + return login_already_logged_in; + } found_user = getuser(&CC->usersupp,username); if (found_user != 0) { @@ -269,24 +266,56 @@ void cmd_user(char *cmdbuf) for (a=0; ausersupp,autoname); - } } + } if (found_user == 0) { if (((CC->nologin)) && (CC->usersupp.axlevel < 6)) { - cprintf("%d %s: Too many users are already online (maximum is %d)\n", - ERROR+MAX_SESSIONS_EXCEEDED, - config.c_nodename,config.c_maxsessions); - } + return login_too_many_users; + } else { strcpy(CC->curr_user,CC->usersupp.fullname); + return login_ok; + } + } + return login_not_found; +} + + + +/* + * USER cmd + */ +void cmd_user(char *cmdbuf) +{ + char username[256]; + int a; + + extract(username,cmdbuf,0); + username[25] = 0; + strproc(username); + + a = CtdlLoginExistingUser(username); + switch(a) { + case login_already_logged_in: + cprintf("%d Already logged in.\n",ERROR); + return; + case login_too_many_users: + cprintf("%d %s: " + "Too many users are already online " + "(maximum is %d)\n", + ERROR+MAX_SESSIONS_EXCEEDED, + config.c_nodename,config.c_maxsessions); + return; + case login_ok: cprintf("%d Password required for %s\n", MORE_DATA,CC->curr_user); - } - } - else { - cprintf("%d %s not found.\n",ERROR,username); - } + return; + case login_not_found: + cprintf("%d %s not found.\n", ERROR, username); + return; + cprintf("%d Internal error\n", ERROR); } +} @@ -316,14 +345,21 @@ void session_startup(void) { /* Run any cleanup routines registered by loadable modules */ PerformSessionHooks(EVT_LOGIN); - cprintf("%d %s|%d|%d|%d|%u|%ld\n",OK,CC->usersupp.fullname,CC->usersupp.axlevel, - CC->usersupp.timescalled,CC->usersupp.posted,CC->usersupp.flags, - CC->usersupp.usernum); usergoto(BASEROOM,0); /* Enter the lobby */ rec_log(CL_LOGIN,CC->curr_user); } +void logged_in_response(void) { + cprintf("%d %s|%d|%d|%d|%u|%ld\n", + OK, CC->usersupp.fullname, CC->usersupp.axlevel, + CC->usersupp.timescalled, CC->usersupp.posted, + CC->usersupp.flags, + CC->usersupp.usernum); +} + + + /* * misc things to be taken care of when a user is logged out */ @@ -401,24 +437,20 @@ static int validpw(uid_t uid, const char *pass) } #endif -void cmd_pass(char *buf) + + +int CtdlTryPassword(char *password) { - char password[256]; int code; - extract(password,buf,0); - if ((CC->logged_in)) { - cprintf("%d Already logged in.\n",ERROR); - return; + return pass_already_logged_in; } - if (!strcmp(CC->curr_user,"")) { - cprintf("%d You must send a name with USER first.\n",ERROR); - return; + if (!strcmp(CC->curr_user, NLI)) { + return pass_no_user; } - if (getuser(&CC->usersupp,CC->curr_user)) { - cprintf("%d Can't find user record!\n",ERROR+INTERNAL_ERROR); - return; + if (getuser(&CC->usersupp, CC->curr_user)) { + return pass_internal_error; } code = (-1); @@ -442,14 +474,44 @@ void cmd_pass(char *buf) if (!code) { (CC->logged_in) = 1; session_startup(); + return pass_ok; } else { - cprintf("%d Wrong password.\n",ERROR); rec_log(CL_BADPW,CC->curr_user); + return pass_wrong_password; } } +void cmd_pass(char *buf) +{ + char password[256]; + int a; + + extract(password, buf, 0); + a = CtdlTryPassword(password); + + switch (a) { + case pass_already_logged_in: + cprintf("%d Already logged in.\n",ERROR); + return; + case pass_no_user: + cprintf("%d You must send a name with USER first.\n", + ERROR); + return; + case pass_wrong_password: + cprintf("%d Wrong password.\n", ERROR); + return; + case pass_ok: + logged_in_response(); + return; + cprintf("%d Can't find user record!\n", + ERROR+INTERNAL_ERROR); + } +} + + + /* * Delete a user record *and* all of its related resources. */ @@ -632,6 +694,7 @@ void cmd_newu(char *cmdbuf) } else if (a==0) { session_startup(); + logged_in_response(); } else { cprintf("%d unknown error\n",ERROR); diff --git a/citadel/user_ops.h b/citadel/user_ops.h index 553e185b3..b151ff18f 100644 --- a/citadel/user_ops.h +++ b/citadel/user_ops.h @@ -44,3 +44,35 @@ int GenerateRelationshipIndex( char *IndexBuf, long RoomID, long RoomGen, long UserID); + +int CtdlLoginExistingUser(char *username); + +/* + * Values which may be returned by CtdlLoginExistingUser() + */ +enum { + pass_ok, + pass_already_logged_in, + pass_no_user, + pass_internal_error, + pass_wrong_password +}; + + + + +int CtdlTryPassword(char *password); + +/* + * Values which may be returned by CtdlTryPassword() + */ +enum { + login_ok, + login_already_logged_in, + login_too_many_users, + login_not_found +}; + + + +