]> arthur.barton.de Git - netatalk.git/blobdiff - etc/uams/uams_randnum.c
Prevent security attack guessing valid server accounts
[netatalk.git] / etc / uams / uams_randnum.c
index eee3459d75f5f97d0156a42b978aa710d00b0ad6..3d3a5bb9b72afc9bf267a03bb3682d9288f28d1f 100644 (file)
@@ -1,5 +1,5 @@
 /* 
- * $Id: uams_randnum.c,v 1.13 2003-06-11 06:29:30 srittau Exp $
+ * $Id: uams_randnum.c,v 1.21 2010-03-30 10:25:49 franklahm Exp $
  *
  * Copyright (c) 1990,1993 Regents of The University of Michigan.
  * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) 
@@ -46,19 +46,13 @@ char *strchr (), *strrchr ();
 #include <atalk/afp.h>
 #include <atalk/uam.h>
 
-#include <des.h>
 
+#include <des.h>
 
 #ifdef USE_CRACKLIB
 #include <crack.h>
 #endif /* USE_CRACKLIB */
 
-#include "crypt.h"
-
-#ifndef __inline__
-#define __inline__
-#endif /* __inline__ */
-
 #define PASSWDLEN 8
 
 static C_Block         seskey;
@@ -73,9 +67,9 @@ static u_int8_t         randbuf[8];
 
 
 /* handle ~/.passwd. courtesy of shirsch@ibm.net. */
-static  __inline__ int home_passwd(const struct passwd *pwd, 
-                                  const char *path, const int pathlen, 
-                                  char *passwd, const int len,
+static  int home_passwd(const struct passwd *pwd, 
+                                  const char *path, const int pathlen _U_
+                                  unsigned char *passwd, const int len,
                                   const int set)
 {
   struct stat st;
@@ -144,14 +138,17 @@ home_passwd_fail:
  * key file: 
  * key (in hex) */
 #define PASSWD_ILLEGAL '*'
+#define unhex(x)  (isdigit(x) ? (x) - '0' : toupper(x) + 10 - 'A')
 static int afppasswd(const struct passwd *pwd, 
                     const char *path, const int pathlen, 
-                    char *passwd, int len, 
+                    unsigned char *passwd, int len, 
                     const int set)
 {
   u_int8_t key[DES_KEY_SZ*2];
   char buf[MAXPATHLEN + 1], *p;
+  Key_schedule schedule;
   FILE *fp;
+  unsigned int i, j;
   int keyfd = -1, err = 0;
   off_t pos;
   
@@ -162,7 +159,7 @@ static int afppasswd(const struct passwd *pwd,
   
   /* open the key file if it exists */
   strcpy(buf, path);
-  if (pathlen < sizeof(buf) - 5) {
+  if (pathlen < (int) sizeof(buf) - 5) {
     strcat(buf, ".key");
     keyfd = open(buf, O_RDONLY);
   } 
@@ -171,7 +168,8 @@ static int afppasswd(const struct passwd *pwd,
   memset(buf, 0, sizeof(buf));
   while (fgets(buf, sizeof(buf), fp)) {
     if ((p = strchr(buf, ':'))) {
-      if (strncmp(buf, pwd->pw_name, p - buf) == 0) {
+      if ( strlen(pwd->pw_name) == (p - buf) &&
+           strncmp(buf, pwd->pw_name, p - buf) == 0) {
        p++;
        if (*p == PASSWD_ILLEGAL) {
          LOG(log_info, logtype_uams, "invalid password entry for %s", pwd->pw_name);
@@ -188,38 +186,48 @@ static int afppasswd(const struct passwd *pwd,
   goto afppasswd_done;
 
 afppasswd_found:
-  if (!set)
-    unhexify(p, sizeof(key), p, sizeof(key));
+  if (!set) {
+    /* convert to binary. */
+    for (i = j = 0; i < sizeof(key); i += 2, j++)
+      p[j] = (unhex(p[i]) << 4) | unhex(p[i + 1]);
+    if (j <= DES_KEY_SZ)
+      memset(p + j, 0, sizeof(key) - j);
+  }
 
   if (keyfd > -1) {
-      size_t len;
-
       /* read in the hex representation of an 8-byte key */
       read(keyfd, key, sizeof(key));
 
       /* convert to binary key */
-      len = strlen((char *) key);
-      unhexify(key, len, key, len);
+      for (i = j = 0; i < strlen((char *) key); i += 2, j++)
+       key[j] = (unhex(key[i]) << 4) | unhex(key[i + 1]);
+      if (j <= DES_KEY_SZ)
+       memset(key + j, 0, sizeof(key) - j);
+      key_sched((C_Block *) key, schedule);
+      memset(key, 0, sizeof(key));
 
       if (set) {
        /* NOTE: this takes advantage of the fact that passwd doesn't
         *       get used after this call if it's being set. */
-       err = encrypt(key, passwd, passwd);
+       ecb_encrypt((C_Block *) passwd, (C_Block *) passwd, schedule,
+                   DES_ENCRYPT);
       } else {
-       err = decrypt(key, p, p);
+       /* decrypt the password */
+       ecb_encrypt((C_Block *) p, (C_Block *) p, schedule, DES_DECRYPT);
       }
-      memset(key, 0, sizeof(key));
-
-      if (err)
-       goto afppasswd_done;
+      memset(&schedule, 0, sizeof(schedule));
   }
 
   if (set) {
+    const unsigned char hextable[] = "0123456789ABCDEF";
     struct flock lock;
     int fd = fileno(fp);
 
     /* convert to hex password */
-    hexify(key, sizeof(key), passwd, DES_KEY_SZ);
+    for (i = j = 0; i < DES_KEY_SZ; i++, j += 2) {
+      key[j] = hextable[(passwd[i] & 0xF0) >> 4];
+      key[j + 1] = hextable[passwd[i] & 0x0F];
+    }
     memcpy(p, key, sizeof(key));
 
     /* get exclusive access to the user's password entry. we don't
@@ -252,7 +260,7 @@ afppasswd_done:
  * depending upon whether or not the password is in ~/.passwd
  * or in a global location */
 static int randpass(const struct passwd *pwd, const char *file,
-                   char *passwd, const int len, const int set) 
+                   unsigned char *passwd, const int len, const int set) 
 {
   int i;
   uid_t uid = geteuid();
@@ -289,45 +297,30 @@ static int randpass(const struct passwd *pwd, const char *file,
   return i;
 }
 
-  
 /* randnum sends an 8-byte number and uses the user's password to
  * check against the encrypted reply. */
-static int randnum_login(void *obj, struct passwd **uam_pwd,
-                        char *ibuf, int ibuflen,
-                        char *rbuf, int *rbuflen)
+static int rand_login(void *obj, char *username, int ulen, struct passwd **uam_pwd _U_,
+                        char *ibuf _U_, size_t ibuflen _U_,
+                        char *rbuf, size_t *rbuflen)
 {
-  char *username, *passwdfile;
-  u_int16_t sessid;
-  int len, ulen, err;
-  
-  *rbuflen = 0;
-  
-  if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, 
-                          (void *) &username, &ulen) < 0)
-    return AFPERR_PARAM;
 
-  len = UAM_PASSWD_FILENAME;
-  if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT, 
-                            (void *) &passwdfile, &len) < 0)
-    return AFPERR_PARAM;
-
-  len = (unsigned char) *ibuf++;
-  if ( len > ulen ) {
-       return AFPERR_PARAM;
-  }
-  memcpy(username, ibuf, len );
-  ibuf += len;
-  username[ len ] = '\0';
-  if ((unsigned long) ibuf & 1) /* padding */
-    ++ibuf;
-  
-  if (( randpwd = uam_getname(username, ulen)) == NULL )
-    return AFPERR_PARAM; /* unknown user */
+  char *passwdfile;
+  u_int16_t sessid;
+  size_t len;
+  int err;
+  if (( randpwd = uam_getname(obj, username, ulen)) == NULL )
+    return AFPERR_NOTAUTH; /* unknown user */
   
   LOG(log_info, logtype_uams, "randnum/rand2num login: %s", username);
   if (uam_checkuser(randpwd) < 0)
     return AFPERR_NOTAUTH;
 
+  len = UAM_PASSWD_FILENAME;
+  if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT,
+                             (void *) &passwdfile, &len) < 0)
+    return AFPERR_PARAM;
+
   if ((err = randpass(randpwd, passwdfile, seskey,
                      sizeof(seskey), 0)) != AFP_OK)
     return err;
@@ -354,10 +347,9 @@ static int randnum_login(void *obj, struct passwd **uam_pwd,
 /* check encrypted reply. we actually setup the encryption stuff
  * here as the first part of randnum and rand2num are identical. */
 static int randnum_logincont(void *obj, struct passwd **uam_pwd,
-                            char *ibuf, int ibuflen
-                            char *rbuf, int *rbuflen)
+                            char *ibuf, size_t ibuflen _U_
+                            char *rbuf _U_, size_t *rbuflen)
 {
-  int err = AFP_OK;
   u_int16_t sessid;
 
   *rbuflen = 0;
@@ -368,10 +360,13 @@ static int randnum_logincont(void *obj, struct passwd **uam_pwd,
 
   ibuf += sizeof(sessid);
 
-  err = encrypt(seskey, randbuf, randbuf);
+  /* encrypt. this saves a little space by using the fact that
+   * des can encrypt in-place without side-effects. */
+  key_sched((C_Block *) seskey, seskeysched);
   memset(seskey, 0, sizeof(seskey));
-  if (err)
-    return err;
+  ecb_encrypt((C_Block *) randbuf, (C_Block *) randbuf,
+              seskeysched, DES_ENCRYPT);
+  memset(&seskeysched, 0, sizeof(seskeysched));
 
   /* test against what the client sent */
   if (memcmp( randbuf, ibuf, sizeof(randbuf) )) { /* != */
@@ -381,7 +376,7 @@ static int randnum_logincont(void *obj, struct passwd **uam_pwd,
 
   memset(randbuf, 0, sizeof(randbuf));
   *uam_pwd = randpwd;
-  return err;
+  return AFP_OK;
 }
 
 
@@ -391,13 +386,11 @@ static int randnum_logincont(void *obj, struct passwd **uam_pwd,
  *    and sends it back as part of the reply.
  */
 static int rand2num_logincont(void *obj, struct passwd **uam_pwd,
-                             char *ibuf, int ibuflen
-                             char *rbuf, int *rbuflen)
+                             char *ibuf, size_t ibuflen _U_
+                             char *rbuf, size_t *rbuflen)
 {
-  int err = AFP_OK;
-  CryptHandle crypt_handle;
   u_int16_t sessid;
-  int i;
+  unsigned int i;
 
   *rbuflen = 0;
 
@@ -413,8 +406,10 @@ static int rand2num_logincont(void *obj, struct passwd **uam_pwd,
     seskey[i] <<= 1;
 
   /* encrypt randbuf */
-  err = encrypt_start(&crypt_handle, seskey);
-  encrypt_do(crypt_handle, randbuf, randbuf);
+  key_sched((C_Block *) seskey, seskeysched);
+  memset(seskey, 0, sizeof(seskey));
+  ecb_encrypt( (C_Block *) randbuf, (C_Block *) randbuf,
+              seskeysched, DES_ENCRYPT);
 
   /* test against client's reply */
   if (memcmp(randbuf, ibuf, sizeof(randbuf))) { /* != */
@@ -426,10 +421,9 @@ static int rand2num_logincont(void *obj, struct passwd **uam_pwd,
   memset(randbuf, 0, sizeof(randbuf));
 
   /* encrypt client's challenge and send back */
-  encrypt_do(crypt_handle, rbuf, ibuf);
-  encrypt_end(crypt_handle);
-  memset(seskey, 0, sizeof(seskey));
-
+  ecb_encrypt( (C_Block *) ibuf, (C_Block *) rbuf,
+              seskeysched, DES_ENCRYPT);
+  memset(&seskeysched, 0, sizeof(seskeysched));
   *rbuflen = sizeof(randbuf);
   
   *uam_pwd = randpwd;
@@ -440,12 +434,13 @@ static int rand2num_logincont(void *obj, struct passwd **uam_pwd,
  * NOTE: an FPLogin must already have completed successfully for this
  *       to work. 
  */
-static int randnum_changepw(void *obj, const char *username, 
+static int randnum_changepw(void *obj, const char *username _U_
                            struct passwd *pwd, char *ibuf,
-                           int ibuflen, char *rbuf, int *rbuflen)
+                           size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen _U_)
 {
     char *passwdfile;
-    int err, len;
+    int err;
+    size_t len;
 
     if (uam_checkuser(pwd) < 0)
       return AFPERR_ACCESS;
@@ -481,7 +476,7 @@ static int randnum_changepw(void *obj, const char *username,
 #endif /* USE_CRACKLIB */
 
     if (!err) 
-      err = randpass(pwd, passwdfile, ibuf + PASSWDLEN, sizeof(seskey), 1);
+        err = randpass(pwd, passwdfile, (unsigned char *)ibuf + PASSWDLEN, sizeof(seskey), 1);
 
     /* zero out some fields */
     memset(&seskeysched, 0, sizeof(seskeysched));
@@ -495,17 +490,81 @@ static int randnum_changepw(void *obj, const char *username,
   return( AFP_OK );
 }
 
+/* randnum login */
+static int randnum_login(void *obj, struct passwd **uam_pwd,
+                        char *ibuf, size_t ibuflen,
+                        char *rbuf, size_t *rbuflen)
+{
+    char *username;
+    size_t len, ulen;
+
+    *rbuflen = 0;
+
+    if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
+                             (void *) &username, &ulen) < 0)
+        return AFPERR_MISC;
+
+    if (ibuflen < 2) {
+        return( AFPERR_PARAM );
+    }
+
+    len = (unsigned char) *ibuf++;
+    ibuflen--;
+    if (!len || len > ibuflen || len > ulen ) {
+        return( AFPERR_PARAM );
+    }
+    memcpy(username, ibuf, len );
+    ibuf += len;
+    ibuflen -=len;
+    username[ len ] = '\0';
+
+    if ((unsigned long) ibuf & 1) { /* pad character */
+        ++ibuf;
+        ibuflen--;
+    }
+    return (rand_login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
+}
+
+/* randnum login ext */
+static int randnum_login_ext(void *obj, char *uname, struct passwd **uam_pwd,
+                        char *ibuf, size_t ibuflen,
+                        char *rbuf, size_t *rbuflen)
+{
+    char       *username;
+    size_t     len, ulen;
+    u_int16_t  temp16;
+
+    *rbuflen = 0;
+
+    if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
+                             (void *) &username, &ulen) < 0)
+        return AFPERR_MISC;
+
+    if (*uname != 3)
+        return AFPERR_PARAM;
+    uname++;
+    memcpy(&temp16, uname, sizeof(temp16));
+    len = ntohs(temp16);
+    if (!len || len > ulen ) {
+        return( AFPERR_PARAM );
+    }
+    memcpy(username, uname +2, len );
+    username[ len ] = '\0';
+    return (rand_login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
+}
+
 static int uam_setup(const char *path)
 {
-  if (uam_register(UAM_SERVER_LOGIN, path, "Randnum exchange", 
-                  randnum_login, randnum_logincont, NULL) < 0)
+  if (uam_register(UAM_SERVER_LOGIN_EXT, path, "Randnum exchange", 
+                  randnum_login, randnum_logincont, NULL, randnum_login_ext) < 0)
     return -1;
-  if (uam_register(UAM_SERVER_LOGIN, path, "2-Way Randnum exchange",
-                  randnum_login, rand2num_logincont, NULL) < 0) {
+
+  if (uam_register(UAM_SERVER_LOGIN_EXT, path, "2-Way Randnum exchange",
+                  randnum_login, rand2num_logincont, NULL, randnum_login_ext) < 0) {
     uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
     return -1;
   }
-    
+
   if (uam_register(UAM_SERVER_CHANGEPW, path, "Randnum Exchange", 
                   randnum_changepw) < 0) {
     uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");