From 7ea13ad3f26b14d70072d397569ed02cf565a7a3 Mon Sep 17 00:00:00 2001 From: Thomas Osterried Date: Sun, 9 Apr 2006 11:49:21 +0000 Subject: md5 and sys authentication via /etc/ax25/bcpasswd and $HOME/bcpasswd. Documented in axspawn.8 Thanks to Christoph for contribution. --- ax25/Makefile.am | 2 +- ax25/access.c | 380 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ ax25/access.h | 19 +++ ax25/axspawn.8 | 74 ++++++++++- ax25/axspawn.c | 197 ++++++++++++++++++++++------ ax25/axspawn.h | 8 ++ ax25/md5.c | 290 ++++++++++++++++++++++++++++++++++++++++++ ax25/md5.h | 65 ++++++++++ 8 files changed, 991 insertions(+), 44 deletions(-) create mode 100644 ax25/access.c create mode 100644 ax25/access.h create mode 100644 ax25/axspawn.h create mode 100644 ax25/md5.c create mode 100644 ax25/md5.h (limited to 'ax25') diff --git a/ax25/Makefile.am b/ax25/Makefile.am index bb78855..a49a084 100644 --- a/ax25/Makefile.am +++ b/ax25/Makefile.am @@ -39,7 +39,7 @@ EXTRA_DIST = $(man_MANS) $(etcfiles) $(varfiles) ax25d_SOURCES = ax25d.c axctl_SOURCES = axctl.c axparms_SOURCES = axparms.c -axspawn_SOURCES = axspawn.c +axspawn_SOURCES = axspawn.c access.c md5.c beacon_SOURCES = beacon.c bpqparms_SOURCES = bpqparms.c mheard_SOURCES = mheard.c diff --git a/ax25/access.c b/ax25/access.c new file mode 100644 index 0000000..0a54bd4 --- /dev/null +++ b/ax25/access.c @@ -0,0 +1,380 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "access.h" +#include "md5.h" +#include "axspawn.h" + +#define CONV_RAND_MAX 0x7fffffff + +#define SYSTEMPW 0 +#define USERPW 1 + +long seed = 1L; + +int conv_rand(void); +void conv_randomize(void); +int conv_random(int num, int base); +char *generate_rand_pw(int len); +void calc_md5_pw (const char *MD5prompt, const char *MD5pw, char *MD5result); +static void char_to_hex(char *c, char *h, int n); + +/*--------------------------------------------------------------------------*/ + +int conv_rand(void) +{ + seed = (1103515245L * seed + 12345) & CONV_RAND_MAX; + return ((int) (seed & 077777)); +} + +/*--------------------------------------------------------------------------*/ + +void conv_randomize(void) +{ + seed = (time(0) & CONV_RAND_MAX); +} + +/*--------------------------------------------------------------------------*/ + +int conv_random(int num, int base) +{ + return (((long) (conv_rand() * time(0)) & CONV_RAND_MAX) % num + base); +} + +/*--------------------------------------------------------------------------*/ + +static void char_to_hex(char *c, char *h, int n) +{ + + int i; + static char *hextable = "0123456789abcdef"; + + for (i = 0; i < n; i++) { + *h++ = hextable[(*c>>4)&0xf]; + *h++ = hextable[*c++ &0xf]; + } + *h = '\0'; +} + +/*--------------------------------------------------------------------------*/ + +char *generate_rand_pw(int len) +{ + static char pass[PASSSIZE+1]; + int i, j; + + pass[0] = 0; + + if (seed == 1L) + conv_randomize(); + + if (len < 1) + return pass; + + if (len > PASSSIZE) + len = PASSSIZE; + + for (i = 0; i < len; i++) { + j = conv_random(10+26*2, 0); + if (j < 10) { + pass[i] = j + '0'; + continue; + } + j -= 10; + if (j < 26) { + pass[i] = j + 'A'; + continue; + } + j -= 26; + pass[i] = j + 'a'; + } + pass[len] = 0; + + return pass; +} + +/*--------------------------------------------------------------------------*/ + +void ask_pw_sys(char *prompt, char *pass_want, char *pw) +{ + char buffer[2048]; + int five_digits[5]; + int pwlen; + int i, j; + + pass_want[0]= 0; + + if (!pw || !*pw) + return; + + pwlen = strlen(pw); + + if (seed == 1L) + conv_randomize(); + + for (i = 0; i < 5; i++) { + j = conv_random(pwlen, 0); + // store generated request-numbers + five_digits[i] = j+1; // pos0 refers as 1 + // store expected string in cp->passwd + pass_want[i] = pw[j]; + } + // and terminate the string + pass_want[i] = 0; + + sprintf(buffer, "\n%s> %d %d %d %d %d\n", prompt, five_digits[0], five_digits[1], five_digits[2], five_digits[3], five_digits[4]); + write_ax25(buffer, strlen(buffer), 1); +} + +/*--------------------------------------------------------------------------*/ + +void ask_pw_md5(char *prompt, char *pass_want, char *pw) +{ +#define SALT_LEN 10 + char buffer[2048]; + char cipher[16]; + char key[256]; +// char *pass; + char *challenge; + +// pass = pw; + pass_want[0]= 0; + + if (!pw || !*pw) + return; + + if (seed == 1L) + conv_randomize(); + + // copy password + strncpy(key, pw, sizeof(key)); + key[sizeof(key)-1] = 0; + + // compute random salt + challenge = generate_rand_pw(SALT_LEN); + + // ask for proper response to this challenge: + sprintf(buffer, "\n%s> [%s]\n", prompt, challenge); + write_ax25(buffer, strlen(buffer), 1); + // compute md5 challenge + calc_md5_pw(challenge, key, cipher); + // store expected answer + char_to_hex(cipher, pass_want, 16); +} + +/*--------------------------------------------------------------------------*/ + +void calc_md5_pw (const char *MD5prompt, const char *MD5pw, char *MD5result) +{ + MD5_CTX context; + short i, n, len; + char buffer[1024]; + + strncpy(buffer, MD5prompt, 10); + buffer[10] = 0; + strcat(buffer, MD5pw); + + MD5Init(&context); + + len = strlen(buffer); + for (i= 0; i < len; i += 16) { + n = (len - i) > 16 ? 16 : (len - i); + MD5Update(&context, buffer+i, n); + } + + MD5Final(&context); + + MD5result[0] = '\0'; + for (i = 0; i < 16; i++) { + MD5result[i] = context.digest[i]; + } +} + +/*--------------------------------------------------------------------------*/ + +void write_example_passwd(char *pwfile, char pwlocation, struct passwd *pw) { + FILE * f; + int i; + + if ((i = open(pwfile, O_CREAT|O_WRONLY|O_TRUNC, (S_IRUSR | S_IWUSR | (pwlocation == SYSTEMPW ? (S_IRGRP /* | S_IWGRP */ ) : 0)))) == -1) + return; + fchown(i, (pwlocation == SYSTEMPW ? 0 : pw->pw_uid), (pwlocation == SYSTEMPW ? 0 : pw->pw_gid)); + close(i); + if ( ! (f = fopen(pwfile, "w")) ) + return; + fprintf(f, "# %s Password file for axspawn\n", (pwlocation == SYSTEMPW ? "System" : "User")); + if (pwlocation == SYSTEMPW) { + fprintf(f, "# disable user self-administered passwords in $HOME/.%s\n", PWFILE); + fprintf(f, "# with the line \"systempasswordonly\"\n"); + fprintf(f, "# systempasswordonly\n"); + } + fprintf(f, "# Examples (sys and md5 passwords may differ):\n"); + fprintf(f, "# md5 stadard (secure) - length: >= %d and <= %d characters\n", MINPWLEN_MD5, PASSSIZE); + fprintf(f, "# %smd5:%s\n", (pwlocation == SYSTEMPW ? "username:" : ""), generate_rand_pw(MINPWLEN_MD5)); + fprintf(f, "# sys/baycom standard (not very secure) - length: >= %d and <= %d characters\n", MINPWLEN_SYS, PASSSIZE); + fprintf(f, "# %ssys:%s\n", (pwlocation == SYSTEMPW ? "username:" : ""), generate_rand_pw(MINPWLEN_SYS)); + fclose(f); +} + +/*--------------------------------------------------------------------------*/ + +char *read_pwd (struct passwd *pw, int *pwtype) +{ + FILE * f = 0; + struct stat statbuf; + char pwfile[PATH_MAX + 1]; + int len; + char pwlocation; + char buf[2048]; + int only_systempw = 0; + char *pass = 0; + char *p_buf; + char *p; + + + for (pwlocation = 0; pwlocation < 2; pwlocation++) { + + if (pwlocation == SYSTEMPW) { + sprintf(pwfile, "/%s/%s", AX25_SYSCONFDIR, PWFILE); + if (stat(pwfile, &statbuf)) { + write_example_passwd(pwfile, pwlocation, pw); + continue; + } + if (!S_ISREG(statbuf.st_mode) || (statbuf.st_mode & (S_IROTH | S_IWOTH))) + continue; + if ( !(f = fopen(pwfile, "r")) ) + continue; + } else { + if (only_systempw) + goto end; + snprintf(pwfile, sizeof(pwfile), "%s/.%s", pw->pw_dir, PWFILE); + pwfile[sizeof(pwfile)-1] = 0; + + if (stat(pwfile, &statbuf)) { + sprintf(buf, "Notice: No .%s file found in your homedirectory (for more secure\n", PWFILE); + write_ax25(buf, strlen(buf), 1); + sprintf(buf, " password authentication than plaintext). Generating example file,\n"); + write_ax25(buf, strlen(buf), 1); + sprintf(buf, " with unique passwords (which may be changed).\n"); + write_ax25(buf, strlen(buf), 1); + sprintf(buf, " Please edit ~/.%s, and enable your prefered authentication type;\n", PWFILE); + write_ax25(buf, strlen(buf), 1); + sprintf(buf, " MD5 is recommended.\n"); + write_ax25(buf, strlen(buf), 1); + write_example_passwd(pwfile, pwlocation, pw); + goto end; + } + if (!S_ISREG(statbuf.st_mode)) { + sprintf(buf, "Error: password file .%s should be a regular file. Skiping..\n", PWFILE); + write_ax25(buf, strlen(buf), 1); + goto end; + } + if (statbuf.st_uid != 0 && statbuf.st_uid != pw->pw_uid) { + sprintf(buf, "Error: your password file .%s is not owned by you. Skiping..\n", PWFILE); + write_ax25(buf, strlen(buf), 1); + goto end; + } + if ((statbuf.st_mode & (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH))) { + sprintf(buf, "WARNING: your password file .%s has wrong permissions.\n", PWFILE); + write_ax25(buf, strlen(buf), 1); + sprintf(buf, " Please change it with\n"); + write_ax25(buf, strlen(buf), 1); + sprintf(buf, " chmod 600 .%s\n", PWFILE); + write_ax25(buf, strlen(buf), 1); + sprintf(buf, " and don't forget to change your password stored in .%s\n", PWFILE); + write_ax25(buf, strlen(buf), 1); + sprintf(buf, " because it may be compromised.\n"); + write_ax25(buf, strlen(buf), 1); + /* go on.. if user takes no action, he always gets this message */ + } + if ( !(f = fopen(pwfile, "r")) ) + goto end; + } + + + for (;;) { + if (!fgets(buf, sizeof(buf), f)) { + fclose(f); + if (pwlocation == SYSTEMPW) + break; + /* perhaps this is too irritating for the user + when the message occurs always. Thus only + write the notice, if cleartext fallback + is disabled by administrative configuration. + */ + if (!((*pwtype) & PW_CLEARTEXT)) { + sprintf(buf, "Failed to find a suitable password in %s\n", pwfile); + write_ax25(buf, strlen(buf), 1); + } + goto end; + } + if ((p = strchr(buf, '\n'))) + *p = 0; + if (!*buf || isspace(*buf & 0xff)) + continue; + if (*buf == '#') + continue; + if (pwlocation == SYSTEMPW) { + if (!Strcasecmp(buf, "systempasswordonly")) { + only_systempw = 1; + continue; + } + if (!(p = strchr(buf, ':'))) + continue; + *p++ = 0; + if (strcmp(pw->pw_name, buf)) + continue; + p_buf = p; + } else { + p_buf = buf; + } + if (!(pass = strchr(p_buf, ':'))) + continue; + *pass++ = 0; + + while (*pass && isspace(*pass & 0xff)) + pass++; + for (p = pass; *p && !isspace(*p & 0xff); p++) ; + *p = 0; + + if ( (*pwtype & PW_MD5) && !Strcasecmp(p_buf, "md5") ) { + fclose(f); + *pwtype = PW_MD5; + goto found; + } else if ( (*pwtype & PW_SYS) && (!Strcasecmp(p_buf, "sys") || !strcmp(p_buf, "baycom")) ) { + *pwtype = PW_SYS; + goto found; + } + } + } +found: + + if (!pass || !*pwtype) + goto end; + + len = strlen(pass); + + if ((*pwtype == PW_SYS && len < MINPWLEN_SYS) || (*pwtype == PW_MD5 && len < MINPWLEN_MD5)) { + sprintf(buf, "Password in in password file too short\n"); + write_ax25(buf, strlen(buf), 1); + goto end; + } + if (strlen(pass) > PASSSIZE) + pass[PASSSIZE] = 0; + + return strdup(pass); + +end: + *pwtype = (((*pwtype) & PW_CLEARTEXT) ? PW_CLEARTEXT : 0); + /* ^ may allow cleartext? - then cleartext, else deny */ + return 0; +} + diff --git a/ax25/access.h b/ax25/access.h new file mode 100644 index 0000000..12e4666 --- /dev/null +++ b/ax25/access.h @@ -0,0 +1,19 @@ +#ifndef ACCESS_H +#define ACCESS_H + +#define PASSSIZE 80 /* for md5 passwords, at least 32 characters */ +#define MINPWLEN_SYS 20 +#define MINPWLEN_MD5 8 + +#define PWFILE "bcpasswd" + +/* PWxxx: set bits */ +#define PW_CLEARTEXT 1 +#define PW_SYS 2 +#define PW_MD5 4 + +void ask_pw_sys(char *prompt, char *pass_want, char *pw); +void ask_pw_md5(char *prompt, char *pass_want, char *pw); +char *read_pwd (struct passwd *pw, int *pwtype); +#endif + diff --git a/ax25/axspawn.8 b/ax25/axspawn.8 index ccb6934..5d6f6b0 100644 --- a/ax25/axspawn.8 +++ b/ax25/axspawn.8 @@ -2,7 +2,7 @@ .SH NAME axspawn \- Allow automatic login to a Linux system. .SH SYNOPSIS -.B axspawn [--wait, -w] +.B axspawn [--pwprompt PR0MPT, -p PR0MPT] [--changeuser, -c] [--rootlogin, -r] [--only-md5] [--wait, -w] .SH DESCRIPTION .LP .B Axspawn @@ -32,11 +32,75 @@ SSID). There must be at least one digit, and max. two digits within the call. The SSID must be within the range of 0 and 15. Please drop me a note if you know a valid Amateur Radio callsign that does not fit this pattern _and_ can be represented correctly in AX.25. +.LP +axspawn also has the well known authentication mechanisms of the AX.25 bbs +.B baycom (sys) +and +.B md5 +standards. +axspawn searches in /etc/ax25/bcpasswd (first) and ~user/.bcpasswd (second) +for a match of the required authentication mechanism and password. +md5 and baycom passwords may differ. md5 passwords gain over baycom passwords. + +Note: you could "lock" special "friends" out by specifying an empty password +in /etc/ax25/bcpasswd (line "n0call:md5:"). -> md5 Passwords are enforced. But +the length is shorter than the minimum (len 8 for md5, len 20 for baycom); +user's password file is not searched because in /etc/ax25/bcpasswd its already +found.. + +Syntax and caveeats for /etc/ax25/bcpasswd: + - Has to be a regular file (no symlink). Not world-readable/writable. + - Example lines: + # Thomas + dl9sau:md5:abcdefgh + # Test + te1st:sys:12345678901234567890 + # root + root:md5:ziz7AoxuAt6jeuthTheexet0uDa9iefuAeph3eelAetahmi0 + # misconfiguration: + thisbadlineisignored + # With this line + systempasswordonly + # .. axspan will not look in user's homedir for his .bcpasswd + +Syntax and caveeats for user's .bcpasswd in his $HOME: + - Has to be a regular file (no symlink). Neither group- nor world- + read-/writable. Has to be owned by the user or uid 0 (root). + - Example lines: + # could be shorter + md5:abcdefgh + # should be longer + sys:12345678901234567890 + .SH OPTIONS .TP 5 +.B -p DB0FHN or --pwprompt DB0FHN +While baycom or md5 password authentication (see above), the password prompt +is set to the first argument (DB0FHN in this example). This may be needed +for some packet-radio terminal programs for detecting the password prompt +properly. +.TP 5 +.B -c, --changeuser +Allow connecting ax25 users to change their username for login. They'll be +asked for their real login name. +.TP 5 +.B -r, --rootlogin +Permit login as user root. Cave: only md5 or baycom style is allowed; no +plaintext password. +.TP 5 +.B --only-md5 +Insist in md5 authentication during login. If no password for the user is +found, or it is not md5, then no other login mechanism is granted. +This option, in combination with -c and -r, may be a useful configuration for +systems where no ax25 user accounts are available, but you as sysop would +like to have a login access for your administrative tasks. +.TP 5 .B -w, --wait -Disables the prompting for a password if the password entry in /etc/passwd -is either a \(lq+\(rq or blank. +Eats the first line the user sends. This feature is useful if you have +TCP VC connects to the same Call+SSID. It is now obsolete, because +ax25d is the right place for this and implements this functionality better. +.TP 5 +Theses are options and not part of the preferences because you _may_ like to have on every interface definition in ax25d.conf (where axspawn is started from) a different behaviour. .SH FILES .nf /etc/passwd @@ -45,6 +109,10 @@ is either a \(lq+\(rq or blank. .br /etc/ax25/axspawn.conf .fi +/etc/ax25/bcpasswd +.fi +~/.bcpasswd +.fi .SH "SEE ALSO" .BR axspawn.conf (5), .BR ax25d (8). diff --git a/ax25/axspawn.c b/ax25/axspawn.c index 9e88a4c..0f55abc 100644 --- a/ax25/axspawn.c +++ b/ax25/axspawn.c @@ -1,6 +1,6 @@ /* * - * $Id: axspawn.c,v 1.4 2005/12/10 12:39:57 dl9sau Exp $ + * $Id: axspawn.c,v 1.5 2006/04/09 11:49:21 dl9sau Exp $ * * axspawn.c - run a program from ax25d. * @@ -185,6 +185,8 @@ #include #include "../pathnames.h" +#include "axspawn.h" +#include "access.h" #define MAXLEN strlen("DB0PRA-15") #define MINLEN strlen("KA9Q") @@ -459,7 +461,6 @@ long wqueue_length = 0L; int encstathuf(char *src, int srclen, char *dest, int *destlen); int decstathuf(char *src, char *dest, int srclen, int *destlen); -int write_ax25(char *s, int len, int kick); /*---------------------------------------------------------------------------*/ @@ -1273,7 +1274,7 @@ void signal_handler(int dummy) int main(int argc, char **argv) { - char call[20], user[20], real_user[20]; + char call[20], user[20], as_user[20]; char buf[2048]; int k, cnt, digits, letters, invalid, ssid, ssidcnt, addrlen; pid_t pid = -1; @@ -1284,7 +1285,6 @@ int main(int argc, char **argv) char *chargv[20]; int envc; char *envp[20]; - char wait_for_tcp; struct utmp ut_line; struct winsize win = { 0, 0, 0, 0}; struct sockaddr_ax25 sax25; @@ -1292,17 +1292,52 @@ int main(int argc, char **argv) struct full_sockaddr_ax25 fsax25; struct sockaddr_rose rose; } sockaddr; - char *protocol; + char *protocol = ""; char is_guest = 0; + char wait_for_tcp = 0; + char changeuser = 0; + char user_changed = 1; + char rootlogin = 0; + int pwtype = 0; + int pwtype_orig = 0; + char prompt[20]; + char *pwd = 0; + + *prompt = 0; digits = letters = invalid = ssid = ssidcnt = 0; - if (argc > 1 && (!strcmp(argv[1],"-w") || !strcmp(argv[1],"--wait"))) - wait_for_tcp = 1; - else - wait_for_tcp = 0; - + for (k = 1; k < argc; k++){ + if (!strcmp(argv[k], "-w") || !strcmp(argv[k], "--wait")) + wait_for_tcp = 1; + if (!strcmp(argv[k], "-c") || !strcmp(argv[k], "--changeuser")) + changeuser = 1; + if (!strcmp(argv[k], "-r") || !strcmp(argv[k], "--rootlogin")) + rootlogin = 1; + if ((!strcmp(argv[k], "-p") || !strcmp(argv[k], "--pwprompt")) && k < argc-1 ) { + strncpy(prompt, argv[k+1], sizeof(prompt)); + prompt[sizeof(prompt)-1] = '\0'; + k++; + } + if (!strcmp(argv[k], "--only-md5")) + pwtype = PW_MD5; + } read_config(); + + if (!pwtype) + pwtype = (PW_CLEARTEXT | PW_SYS | PW_MD5); + pwtype_orig = pwtype; + if (!*prompt) { + if (gethostname(buf, sizeof(buf)) < 0) { + strcpy(prompt, "Check"); + } else { + if ((p = strchr(buf, '.'))) + *p = 0; + strncpy(prompt, buf, sizeof(prompt)); + prompt[sizeof(prompt)-1] = 0; + } + } + strupr(prompt); openlog("axspawn", LOG_PID, LOG_DAEMON); @@ -1393,54 +1428,128 @@ int main(int argc, char **argv) return 1; } - strcpy(user, call); - strlwr(user); - p = strchr(user, '-'); - if (p) *p = '\0'; - strcpy(real_user, user); - if (wait_for_tcp) { /* incoming TCP/IP connection? */ if (read_ax25(buf, sizeof(buf)) < 0) exit(0); } + + strcpy(user, call); + strlwr(user); + p = strchr(user, '-'); + if (p) *p = '\0'; + + *as_user = 0; + if (changeuser) { + char *p_buf; + sprintf(buf, "Login (%s): ", user); + write_ax25(buf, strlen(buf), 1); + if ((cnt = read_ax25(buf, sizeof(buf)-1)) < 0) + exit(1); + buf[cnt] = 0; + + /* skip leading blanks */ + for (p_buf = buf; *p_buf && *p_buf != '\n' && !isalnum(*p_buf & 0xff); p_buf++) ; + /* skip trailing junk, blanks, \n, .. */ + for (p = p_buf; *p && isalnum(*p & 0xff); p++) ; + *p = 0; + + if (*p_buf) { + strncpy(as_user, p_buf, sizeof(as_user)); + as_user[sizeof(as_user)-1] = 0; + user_changed = 1; + } + } - pw = getpwnam(user); + if (!*as_user) + strcpy(as_user, user); + pw = getpwnam(as_user); + endpwent(); if (pw == NULL) { + if (user_changed) { + syslog(LOG_NOTICE, "%s (callsign: %s) not found in /etc/passwd\n", as_user, call); + sleep(EXITDELAY); + return 1; + } + if (policy_add_user) { - new_user(user); - pw = getpwnam(user); + new_user(as_user); + pw = getpwnam(as_user); + endpwent(); } if (pw == NULL && policy_guest) { - strcpy(real_user,guest); + strcpy(as_user,guest); pw = getpwnam(guest); + endpwent(); is_guest = 1; - - if (! (pw && pw->pw_uid && pw->pw_gid) ) - { - write_ax25(MSG_NOTINDBF, sizeof(MSG_NOTINDBF), 1); - syslog(LOG_NOTICE, "%s (callsign: %s) not found in /etc/passwd\n", user, call); - sleep(EXITDELAY); - return 1; - } } } + if (!pw) { + write_ax25(MSG_NOTINDBF, sizeof(MSG_NOTINDBF), 1); + syslog(LOG_NOTICE, "%s (callsign: %s) not found in /etc/passwd\n", as_user, call); + sleep(EXITDELAY); + return 1; + } - endpwent(); - - if (pw->pw_uid == 0 || pw->pw_gid == 0) + if (!rootlogin && (pw->pw_uid == 0 || pw->pw_gid == 0)) { write_ax25(MSG_NOCALL, sizeof(MSG_NOCALL), 1); - syslog(LOG_NOTICE, "root login of %s (callsign: %s) denied\n", user, call); + syslog(LOG_NOTICE, "root login of %s (callsign: %s) denied\n", as_user, call); sleep(EXITDELAY); return 1; } +again: + if (!(pwd = read_pwd(pw, &pwtype))) { + if (!pwtype || pwtype != PW_CLEARTEXT) { + sleep (EXITDELAY); + return 1; + } + } + + if (pwtype != PW_CLEARTEXT) { + char pass_want[PASSSIZE+1]; + if (pwtype == PW_MD5) + ask_pw_md5(prompt, pass_want, pwd); + else + ask_pw_sys(prompt, pass_want, pwd); + + cnt = read_ax25(buf, sizeof(buf)-1); + if (cnt <= 0) { + sprintf(buf,"no response\n"); + write_ax25(buf, strlen(buf),1); + sleep (EXITDELAY); + return -11; + } + buf[cnt] = 0; + if ((p = strchr(buf, '\n'))) + *p = 0; + if ((pwtype & PW_MD5) && !strcmp(buf, "sys") && (pwtype_orig & PW_SYS)) { + pwtype = (pwtype_orig & ~PW_MD5); + goto again; + } + if (!strstr(buf, pass_want)) { + sprintf(buf,"authentication failed\n"); + write_ax25(buf, strlen(buf), 1); + sleep (EXITDELAY); + return -11; + } + free(pwd); + } else { + if (pw->pw_uid == 0 || pw->pw_gid == 0) { + sprintf(buf, "Sorry, root logins are only allowed with md5- or baycom-password!\n"); + write_ax25(buf, strlen(buf), 1); + syslog(LOG_NOTICE, "root login of %s (callsign: %s) denied (only with md5- or baycom-Login)!\n", user, call); + sleep(EXITDELAY); + return 1; + } + } + /* * associate UID with callsign (or vice versa?) */ @@ -1527,18 +1636,26 @@ int main(int argc, char **argv) * out (abuse, ..) by changing his gid in passwd to for e.g. * 65534 (nogroup). */ - if (pwcheck == 2 || (pwcheck == 3 && (pw->pw_gid == user_gid || is_guest)) || !strcmp(pw->pw_passwd, "+")) + if (pwtype != PW_CLEARTEXT /* PW_SYS or PW_MD5 are already authenticated */ + || pwcheck == 2 || (pwcheck == 3 && (pw->pw_gid == user_gid || is_guest)) || !strcmp(pw->pw_passwd, "+")) chargv[chargc++] = "-f"; - chargv[chargc++] = real_user; + chargv[chargc++] = as_user; chargv[chargc] = NULL; envc = 0; - envp[envc] = (char *) malloc(30); - sprintf(envp[envc++], "AXCALL=%s", call); - envp[envc] = (char *) malloc(30); - sprintf(envp[envc++], "CALL=%s", user); - envp[envc] = (char *) malloc(30); - sprintf(envp[envc++], "PROTOCOL=%s", protocol); + if ((envp[envc] = (char *) malloc(30))) + sprintf(envp[envc++], "AXCALL=%s", call); + if ((envp[envc] = (char *) malloc(30))) + sprintf(envp[envc++], "CALL=%s", user); + if ((envp[envc] = (char *) malloc(30))) + sprintf(envp[envc++], "PROTOCOL=%s", protocol); + if ((envp[envc] = (char *) malloc(30))) + sprintf(envp[envc++], "TERM=dumb"); /* SuSE bug (dump - tsts) */ + /* other useful defaults */ + if ((envp[envc] = (char *) malloc(30))) + sprintf(envp[envc++], "EDITOR=/usr/bin/ex"); + if ((envp[envc] = (char *) malloc(30))) + sprintf(envp[envc++], "LESS=-d -E -F"); envp[envc] = NULL; execve(chargv[0], chargv, envp); diff --git a/ax25/axspawn.h b/ax25/axspawn.h new file mode 100644 index 0000000..3d94dc2 --- /dev/null +++ b/ax25/axspawn.h @@ -0,0 +1,8 @@ +#ifndef AXSPAWN_H +#define AXSPAWN_H + +#define EXITDELAY 10 +int write_ax25(char *s, int len, int kick); +int Strcasecmp(const char *s1, const char *s2); + +#endif diff --git a/ax25/md5.c b/ax25/md5.c new file mode 100644 index 0000000..8bd0d11 --- /dev/null +++ b/ax25/md5.c @@ -0,0 +1,290 @@ + +/* + *********************************************************************** + ** md5.c -- the source code for MD5 routines ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 1/91 SRD,AJ,BSK,JT Reference C ver., 7/10 constant corr. ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +#include "md5.h" + +/* + *********************************************************************** + ** Message-digest routines: ** + ** To form the message digest for a message M ** + ** (1) Initialize a context buffer mdContext using MD5Init ** + ** (2) Call MD5Update on mdContext and M ** + ** (3) Call MD5Final on mdContext ** + ** The message digest is now in mdContext->digest[0...15] ** + *********************************************************************** + */ + +/* forward declaration */ +static void Transform(UINT4 *buf, UINT4 *in); + +static unsigned char PADDING[64] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* F, G, H and I are basic MD5 functions */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s, ac) \ + {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) \ + {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) \ + {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) \ + {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* The routine MD5Init initializes the message-digest context + mdContext. All fields are set to zero. + */ +void MD5Init(MD5_CTX *mdContext) +{ + mdContext->i[0] = mdContext->i[1] = (UINT4)0; + + /* Load magic initialization constants. + */ + mdContext->buf[0] = (UINT4)0x67452301; + mdContext->buf[1] = (UINT4)0xefcdab89; + mdContext->buf[2] = (UINT4)0x98badcfe; + mdContext->buf[3] = (UINT4)0x10325476; +} + +/* The routine MD5Update updates the message-digest context to + account for the presence of each of the characters inBuf[0..inLen-1] + in the message whose digest is being computed. + */ +void MD5Update(MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen) +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* update number of bits */ + if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0]) + mdContext->i[1]++; + mdContext->i[0] += ((UINT4)inLen << 3); + mdContext->i[1] += ((UINT4)inLen >> 29); + + while (inLen--) { + /* add new character to buffer, increment mdi */ + mdContext->in[mdi++] = *inBuf++; + + /* transform if necessary */ + if (mdi == 0x40) { + for (i = 0, ii = 0; i < 16; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + mdi = 0; + } + } +} + +/* The routine MD5Final terminates the message-digest computation and + ends with the desired message digest in mdContext->digest[0...15]. + */ +void MD5Final(MD5_CTX *mdContext) +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + unsigned int padLen; + + /* save number of bits */ + in[14] = mdContext->i[0]; + in[15] = mdContext->i[1]; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* pad out to 56 mod 64 */ + padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); + MD5Update (mdContext, PADDING, padLen); + + /* append length in bits and transform */ + for (i = 0, ii = 0; i < 14; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + + /* store buffer in digest */ + for (i = 0, ii = 0; i < 4; i++, ii += 4) { + mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF); + mdContext->digest[ii+1] = + (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); + mdContext->digest[ii+2] = + (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); + mdContext->digest[ii+3] = + (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); + } +} + +/* Basic MD5 step. Transforms buf based on in. + */ +static void Transform(UINT4 *buf, UINT4 *in) +{ + UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + FF ( a, b, c, d, in[ 0], S11, 3614090360UL); /* 1 */ + FF ( d, a, b, c, in[ 1], S12, 3905402710UL); /* 2 */ + FF ( c, d, a, b, in[ 2], S13, 606105819UL); /* 3 */ + FF ( b, c, d, a, in[ 3], S14, 3250441966UL); /* 4 */ + FF ( a, b, c, d, in[ 4], S11, 4118548399UL); /* 5 */ + FF ( d, a, b, c, in[ 5], S12, 1200080426UL); /* 6 */ + FF ( c, d, a, b, in[ 6], S13, 2821735955UL); /* 7 */ + FF ( b, c, d, a, in[ 7], S14, 4249261313UL); /* 8 */ + FF ( a, b, c, d, in[ 8], S11, 1770035416UL); /* 9 */ + FF ( d, a, b, c, in[ 9], S12, 2336552879UL); /* 10 */ + FF ( c, d, a, b, in[10], S13, 4294925233UL); /* 11 */ + FF ( b, c, d, a, in[11], S14, 2304563134UL); /* 12 */ + FF ( a, b, c, d, in[12], S11, 1804603682UL); /* 13 */ + FF ( d, a, b, c, in[13], S12, 4254626195UL); /* 14 */ + FF ( c, d, a, b, in[14], S13, 2792965006UL); /* 15 */ + FF ( b, c, d, a, in[15], S14, 1236535329UL); /* 16 */ + + /* Round 2 */ +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG ( a, b, c, d, in[ 1], S21, 4129170786UL); /* 17 */ + GG ( d, a, b, c, in[ 6], S22, 3225465664UL); /* 18 */ + GG ( c, d, a, b, in[11], S23, 643717713UL); /* 19 */ + GG ( b, c, d, a, in[ 0], S24, 3921069994UL); /* 20 */ + GG ( a, b, c, d, in[ 5], S21, 3593408605UL); /* 21 */ + GG ( d, a, b, c, in[10], S22, 38016083UL); /* 22 */ + GG ( c, d, a, b, in[15], S23, 3634488961UL); /* 23 */ + GG ( b, c, d, a, in[ 4], S24, 3889429448UL); /* 24 */ + GG ( a, b, c, d, in[ 9], S21, 568446438UL); /* 25 */ + GG ( d, a, b, c, in[14], S22, 3275163606UL); /* 26 */ + GG ( c, d, a, b, in[ 3], S23, 4107603335UL); /* 27 */ + GG ( b, c, d, a, in[ 8], S24, 1163531501UL); /* 28 */ + GG ( a, b, c, d, in[13], S21, 2850285829UL); /* 29 */ + GG ( d, a, b, c, in[ 2], S22, 4243563512UL); /* 30 */ + GG ( c, d, a, b, in[ 7], S23, 1735328473UL); /* 31 */ + GG ( b, c, d, a, in[12], S24, 2368359562UL); /* 32 */ + + /* Round 3 */ +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH ( a, b, c, d, in[ 5], S31, 4294588738UL); /* 33 */ + HH ( d, a, b, c, in[ 8], S32, 2272392833UL); /* 34 */ + HH ( c, d, a, b, in[11], S33, 1839030562UL); /* 35 */ + HH ( b, c, d, a, in[14], S34, 4259657740UL); /* 36 */ + HH ( a, b, c, d, in[ 1], S31, 2763975236UL); /* 37 */ + HH ( d, a, b, c, in[ 4], S32, 1272893353UL); /* 38 */ + HH ( c, d, a, b, in[ 7], S33, 4139469664UL); /* 39 */ + HH ( b, c, d, a, in[10], S34, 3200236656UL); /* 40 */ + HH ( a, b, c, d, in[13], S31, 681279174UL); /* 41 */ + HH ( d, a, b, c, in[ 0], S32, 3936430074UL); /* 42 */ + HH ( c, d, a, b, in[ 3], S33, 3572445317UL); /* 43 */ + HH ( b, c, d, a, in[ 6], S34, 76029189UL); /* 44 */ + HH ( a, b, c, d, in[ 9], S31, 3654602809UL); /* 45 */ + HH ( d, a, b, c, in[12], S32, 3873151461UL); /* 46 */ + HH ( c, d, a, b, in[15], S33, 530742520UL); /* 47 */ + HH ( b, c, d, a, in[ 2], S34, 3299628645UL); /* 48 */ + + /* Round 4 */ +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II ( a, b, c, d, in[ 0], S41, 4096336452UL); /* 49 */ + II ( d, a, b, c, in[ 7], S42, 1126891415UL); /* 50 */ + II ( c, d, a, b, in[14], S43, 2878612391UL); /* 51 */ + II ( b, c, d, a, in[ 5], S44, 4237533241UL); /* 52 */ + II ( a, b, c, d, in[12], S41, 1700485571UL); /* 53 */ + II ( d, a, b, c, in[ 3], S42, 2399980690UL); /* 54 */ + II ( c, d, a, b, in[10], S43, 4293915773UL); /* 55 */ + II ( b, c, d, a, in[ 1], S44, 2240044497UL); /* 56 */ + II ( a, b, c, d, in[ 8], S41, 1873313359UL); /* 57 */ + II ( d, a, b, c, in[15], S42, 4264355552UL); /* 58 */ + II ( c, d, a, b, in[ 6], S43, 2734768916UL); /* 59 */ + II ( b, c, d, a, in[13], S44, 1309151649UL); /* 60 */ + II ( a, b, c, d, in[ 4], S41, 4149444226UL); /* 61 */ + II ( d, a, b, c, in[11], S42, 3174756917UL); /* 62 */ + II ( c, d, a, b, in[ 2], S43, 718787259UL); /* 63 */ + II ( b, c, d, a, in[ 9], S44, 3951481745UL); /* 64 */ + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +/* + *********************************************************************** + ** End of md5.c ** + ******************************** (cut) ******************************** + */ diff --git a/ax25/md5.h b/ax25/md5.h new file mode 100644 index 0000000..2bf3867 --- /dev/null +++ b/ax25/md5.h @@ -0,0 +1,65 @@ + +/* + *********************************************************************** + ** md5.h -- header file for implementation of MD5 ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version ** + ** Revised (for MD5): RLR 4/27/91 ** + ** -- G modified to have y&~z instead of y&z ** + ** -- FF, GG, HH modified to add in last register done ** + ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 ** + ** -- distinct additive constant for each step ** + ** -- round 4 added, working mod 7 ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +#ifndef MD5_H +#define MD5_H + +/* typedef a 32-bit type */ +typedef unsigned long int UINT4; + +/* Data structure for MD5 (Message-Digest) computation */ +typedef struct { + UINT4 i[2]; /* number of _bits_ handled mod 2^64 */ + UINT4 buf[4]; /* scratch buffer */ + unsigned char in[64]; /* input buffer */ + unsigned char digest[16]; /* actual digest after MD5Final call */ +} MD5_CTX; + +void MD5Init(MD5_CTX *mdContext); +void MD5Update(MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen); +void MD5Final(MD5_CTX *mdContext); + +#endif + +/* + *********************************************************************** + ** End of md5.h ** + ******************************** (cut) ******************************** + */ -- cgit v1.2.3