$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
Fri Jul 10 1998 Art Cancro <ajc@uncnsrd.mt-kisco.ny.us>
* Initial CVS import
+
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;
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);
#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.
*/
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;
}
char *Dynamic_Module_Init(void)
{
- CtdlRegisterServiceHook(2525,
+ SYM_SMTP = CtdlGetDynamicSymbol();
+ CtdlRegisterServiceHook(25,
smtp_greeting,
smtp_command_loop);
return "$Id$";
#define HOUSEKEEPING_WAKEUP 60
+#define NLI "(not logged in)"
+
+
/*** STRUCTURE SIZE VARIABLES ***/
/* You may NOT change this value once you set up your system. */
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.
}
+ /*
+ * 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.
*/
#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)
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;
+ }
+ }
+}
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,'|')
/*
- * 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) {
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);
}
+}
/* 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
*/
}
#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);
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.
*/
}
else if (a==0) {
session_startup();
+ logged_in_response();
}
else {
cprintf("%d unknown error\n",ERROR);
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
+};
+
+
+
+