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/access.c | 380 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 380 insertions(+) create mode 100644 ax25/access.c (limited to 'ax25/access.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; +} + -- cgit v1.2.3