* Split cmd_user() and cmd_pass() into frontend/backend functions
authorArt Cancro <ajc@citadel.org>
Thu, 9 Dec 1999 05:01:15 +0000 (05:01 +0000)
committerArt Cancro <ajc@citadel.org>
Thu, 9 Dec 1999 05:01:15 +0000 (05:01 +0000)
* serv_smtp: implemented AUTH LOGIN for client authentication

citadel/ChangeLog
citadel/citserver.c
citadel/serv_smtp.c
citadel/sysconfig.h
citadel/sysdep.c
citadel/tools.c
citadel/tools.h
citadel/user_ops.c
citadel/user_ops.h

index d9d64edecaaff11b028b2b96ceedc08641447352..e5961c534aed046f5882331e4e5b964ba3f574f9 100644 (file)
@@ -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 <bryant@cs.usm.maine.edu>
 
 Fri Jul 10 1998 Art Cancro <ajc@uncnsrd.mt-kisco.ny.us>
        * Initial CVS import 
+
index 7b8cc1af8b34c952698e273f2b6f47559b9c5c29..e9573ad588489b0284feea018bc1251db4924eeb 100644 (file)
@@ -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);
index 87aef1acba845cd246834517110e0698cd65a1eb..6d03178053cd4c57ab008c00d76233f1ba303ccd 100644 (file)
 #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;
 
 
 /*
 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$";
index 10796413d7195ac68062fb01712219133ca99eca..a32afa728e648a34fddb88998ad96ed56ff05edf 100644 (file)
@@ -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.      */
index b5ad3bc4475e5163c144546c15460f5c51cd568e..63b89bebf4680dea07d76ec05fea4d80a0c64a47 100644 (file)
@@ -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.
         */
index 0b7e1aaf720ffa6d7d4dea6161e5d72d25f22163..0a87ebd5f0f61e9fdfed6adfa35c758412725d04 100644 (file)
@@ -8,6 +8,13 @@
 #include <string.h>
 #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;
+       }
+    }
+}
index 7024f3117a7e0ae4deb65cdc1be9538c922a8562..a2ae480985cef7ad0c9f36e1c3d494ec199c89f8 100644 (file)
@@ -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,'|')
index dc83df311239ee8b9410004ed876aeaee94ae09c..3ed2d286894123533c0f9b283a42fbc2bcb5df34 100644 (file)
@@ -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; a<strlen(autoname); ++a)
                                if (autoname[a]==',') autoname[a]=0;
                        found_user = getuser(&CC->usersupp,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);
index 553e185b3807d3c31f1fff43da767026cff14072..b151ff18f1fce7045333add6cb512af2e2f80893 100644 (file)
@@ -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
+};
+
+
+
+