summaryrefslogtreecommitdiffstats
path: root/ax25
diff options
context:
space:
mode:
authorThomas Osterried <thomas@osterried.de>2006-04-09 11:49:21 +0000
committerThomas Osterried <thomas@osterried.de>2006-04-09 11:49:21 +0000
commit7ea13ad3f26b14d70072d397569ed02cf565a7a3 (patch)
tree0096f960266ae277f5247557b191a5941bd07c47 /ax25
parent3cbf76b0d19b003402ae69c0f9a01e03260af3c3 (diff)
md5 and sys authentication via /etc/ax25/bcpasswd and $HOME/bcpasswd.
Documented in axspawn.8 Thanks to Christoph <dk2crn> for contribution.
Diffstat (limited to 'ax25')
-rw-r--r--ax25/Makefile.am2
-rw-r--r--ax25/access.c380
-rw-r--r--ax25/access.h19
-rw-r--r--ax25/axspawn.874
-rw-r--r--ax25/axspawn.c197
-rw-r--r--ax25/axspawn.h8
-rw-r--r--ax25/md5.c290
-rw-r--r--ax25/md5.h65
8 files changed, 991 insertions, 44 deletions
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 <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <time.h>
+#include <limits.h>
+#include <pwd.h>
+#include <fcntl.h>
+
+#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 <netax25/axlib.h>
#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) ********************************
+ */