]> arthur.barton.de Git - netatalk.git/blob - etc/uams/uams_randnum.c
8adebec705519d939508fdf96f435a96d9f0287f
[netatalk.git] / etc / uams / uams_randnum.c
1 /* 
2  * $Id: uams_randnum.c,v 1.19 2009-10-15 11:39:48 didg Exp $
3  *
4  * Copyright (c) 1990,1993 Regents of The University of Michigan.
5  * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) 
6  * All Rights Reserved.  See COPYRIGHT.
7  */
8
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif /* HAVE_CONFIG_H */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15
16 /* STDC check */
17 #if STDC_HEADERS
18 #include <string.h>
19 #else /* STDC_HEADERS */
20 #ifndef HAVE_STRCHR
21 #define strchr index
22 #define strrchr index
23 #endif /* HAVE_STRCHR */
24 char *strchr (), *strrchr ();
25 #ifndef HAVE_MEMCPY
26 #define memcpy(d,s,n) bcopy ((s), (d), (n))
27 #define memmove(d,s,n) bcopy ((s), (d), (n))
28 #endif /* ! HAVE_MEMCPY */
29 #endif /* STDC_HEADERS */
30
31 #ifdef HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif /* HAVE_UNISTD_H */
34 #ifdef HAVE_FCNTL_H
35 #include <fcntl.h>
36 #endif /* HAVE_FCNTL_H */
37 #include <ctype.h>
38 #include <pwd.h>
39 #include <sys/stat.h>
40 #include <sys/param.h>
41
42 #include <atalk/logger.h>
43
44 #include <netatalk/endian.h>
45
46 #include <atalk/afp.h>
47 #include <atalk/uam.h>
48
49
50 #include <des.h>
51
52 #ifdef USE_CRACKLIB
53 #include <crack.h>
54 #endif /* USE_CRACKLIB */
55
56 #define PASSWDLEN 8
57
58 static C_Block          seskey;
59 static Key_schedule     seskeysched;
60 static struct passwd    *randpwd;
61 static u_int8_t         randbuf[8];
62
63 /* hash to a 16-bit number. this will generate completely harmless 
64  * warnings on 64-bit machines. */
65 #define randhash(a) (((((unsigned long) a) >> 8) ^ \
66                       ((unsigned long)a)) & 0xffff)
67
68
69 /* handle ~/.passwd. courtesy of shirsch@ibm.net. */
70 static  int home_passwd(const struct passwd *pwd, 
71                                    const char *path, const int pathlen _U_, 
72                                    unsigned char *passwd, const int len,
73                                    const int set)
74 {
75   struct stat st;
76   int fd, i;
77   
78   if ( (fd = open(path, (set) ? O_WRONLY : O_RDONLY)) < 0 ) {
79     LOG(log_error, logtype_uams, "Failed to open %s", path);
80     return AFPERR_ACCESS;
81   }
82
83   if ( fstat( fd, &st ) < 0 ) 
84     goto home_passwd_fail;
85   
86   /* If any of these are true, disallow login: 
87    * - not a regular file
88    * - gid or uid don't match user
89    * - anyone else has permissions of any sort
90    */
91   if (!S_ISREG(st.st_mode) || (pwd->pw_uid != st.st_uid) ||
92       (pwd->pw_gid != st.st_gid) ||
93       (st.st_mode & ( S_IRWXG | S_IRWXO )) ) {
94     LOG(log_info, logtype_uams, "Insecure permissions found for %s.", path);
95     goto home_passwd_fail;
96   }
97
98   /* get the password */
99   if (set) {
100     if (write(fd, passwd, len) < 0) {
101       LOG(log_error, logtype_uams, "Failed to write to %s", path );
102       goto home_passwd_fail;
103     }
104   } else {
105     if (read(fd, passwd, len) < 0) {
106       LOG(log_error, logtype_uams, "Failed to read from %s", path );
107       goto home_passwd_fail;
108     }
109   
110   /* get rid of pesky characters */
111   for (i = 0; i < len; i++)
112     if ((passwd[i] != ' ') && isspace(passwd[i]))
113       passwd[i] = '\0';
114   }
115
116   close(fd);
117   return AFP_OK;
118
119 home_passwd_fail:
120   close(fd);
121   return AFPERR_ACCESS;
122 }
123
124
125
126 /* 
127  * handle /path/afppasswd with an optional key file. we're a lot more
128  * trusting of this file. NOTE: we use our own password entry writing
129  * bits as we want to avoid tromping over global variables. in addition,
130  * we look for a key file and use that if it's there. here are the 
131  * formats: 
132  * password file:
133  * username:password:last login date:failedcount
134  *
135  * password is just the hex equivalent of either the ASCII password
136  * (if the key file doesn't exist) or the des encrypted password.
137  *
138  * key file: 
139  * key (in hex) */
140 #define PASSWD_ILLEGAL '*'
141 #define unhex(x)  (isdigit(x) ? (x) - '0' : toupper(x) + 10 - 'A')
142 static int afppasswd(const struct passwd *pwd, 
143                      const char *path, const int pathlen, 
144                      unsigned char *passwd, int len, 
145                      const int set)
146 {
147   u_int8_t key[DES_KEY_SZ*2];
148   char buf[MAXPATHLEN + 1], *p;
149   Key_schedule  schedule;
150   FILE *fp;
151   unsigned int i, j;
152   int keyfd = -1, err = 0;
153   off_t pos;
154   
155   if ((fp = fopen(path, (set) ? "r+" : "r")) == NULL) {
156     LOG(log_error, logtype_uams, "Failed to open %s", path);
157     return AFPERR_ACCESS;
158   }
159   
160   /* open the key file if it exists */
161   strcpy(buf, path);
162   if (pathlen < (int) sizeof(buf) - 5) {
163     strcat(buf, ".key");
164     keyfd = open(buf, O_RDONLY);
165   } 
166   
167   pos = ftell(fp);
168   memset(buf, 0, sizeof(buf));
169   while (fgets(buf, sizeof(buf), fp)) {
170     if ((p = strchr(buf, ':'))) {
171       if ( strlen(pwd->pw_name) == (p - buf) &&
172            strncmp(buf, pwd->pw_name, p - buf) == 0) {
173         p++;
174         if (*p == PASSWD_ILLEGAL) {
175           LOG(log_info, logtype_uams, "invalid password entry for %s", pwd->pw_name);
176           err = AFPERR_ACCESS;
177           goto afppasswd_done;
178         }
179         goto afppasswd_found;
180       }
181     }
182     pos = ftell(fp);
183     memset(buf, 0, sizeof(buf));
184   }
185   err = AFPERR_PARAM;
186   goto afppasswd_done;
187
188 afppasswd_found:
189   if (!set) {
190     /* convert to binary. */
191     for (i = j = 0; i < sizeof(key); i += 2, j++)
192       p[j] = (unhex(p[i]) << 4) | unhex(p[i + 1]);
193     if (j <= DES_KEY_SZ)
194       memset(p + j, 0, sizeof(key) - j);
195   }
196
197   if (keyfd > -1) {
198       /* read in the hex representation of an 8-byte key */
199       read(keyfd, key, sizeof(key));
200
201       /* convert to binary key */
202       for (i = j = 0; i < strlen((char *) key); i += 2, j++)
203         key[j] = (unhex(key[i]) << 4) | unhex(key[i + 1]);
204       if (j <= DES_KEY_SZ)
205         memset(key + j, 0, sizeof(key) - j);
206       key_sched((C_Block *) key, schedule);
207       memset(key, 0, sizeof(key));
208
209       if (set) {
210         /* NOTE: this takes advantage of the fact that passwd doesn't
211          *       get used after this call if it's being set. */
212         ecb_encrypt((C_Block *) passwd, (C_Block *) passwd, schedule,
213                     DES_ENCRYPT);
214       } else {
215         /* decrypt the password */
216         ecb_encrypt((C_Block *) p, (C_Block *) p, schedule, DES_DECRYPT);
217       }
218       memset(&schedule, 0, sizeof(schedule));
219   }
220
221   if (set) {
222     const unsigned char hextable[] = "0123456789ABCDEF";
223     struct flock lock;
224     int fd = fileno(fp);
225
226     /* convert to hex password */
227     for (i = j = 0; i < DES_KEY_SZ; i++, j += 2) {
228       key[j] = hextable[(passwd[i] & 0xF0) >> 4];
229       key[j + 1] = hextable[passwd[i] & 0x0F];
230     }
231     memcpy(p, key, sizeof(key));
232
233     /* get exclusive access to the user's password entry. we don't
234      * worry so much on reads. in the worse possible case there, the 
235      * user will just need to re-enter their password. */
236     lock.l_type = F_WRLCK;
237     lock.l_start = pos;
238     lock.l_len = 1;
239     lock.l_whence = SEEK_SET;
240
241     fseek(fp, pos, SEEK_SET);
242     fcntl(fd, F_SETLKW, &lock);
243     fwrite(buf, p - buf + sizeof(key), 1, fp);
244     lock.l_type = F_UNLCK;
245     fcntl(fd, F_SETLK, &lock);
246   } else 
247     memcpy(passwd, p, len);
248
249   memset(buf, 0, sizeof(buf));
250
251 afppasswd_done:
252   if (keyfd > -1)
253     close(keyfd);
254   fclose(fp);
255   return err;
256 }
257
258
259 /* this sets the uid. it needs to do slightly different things
260  * depending upon whether or not the password is in ~/.passwd
261  * or in a global location */
262 static int randpass(const struct passwd *pwd, const char *file,
263                     unsigned char *passwd, const int len, const int set) 
264 {
265   int i;
266   uid_t uid = geteuid();
267
268   /* Build pathname to user's '.passwd' file */
269   i = strlen(file);
270   if (*file == '~') {
271     char path[MAXPATHLEN + 1];
272
273     if ( (strlen(pwd->pw_dir) + i - 1) > MAXPATHLEN)
274     return AFPERR_PARAM;
275   
276     strcpy(path,  pwd->pw_dir );
277     strcat(path, "/" );
278     strcat(path, file + 2);
279     if (!uid)
280       seteuid(pwd->pw_uid); /* change ourselves to the user */
281     i = home_passwd(pwd, path, i, passwd, len, set);
282     if (!uid)
283       seteuid(0); /* change ourselves back to root */
284     return i;
285   } 
286
287   if (i > MAXPATHLEN)
288     return AFPERR_PARAM;
289
290   /* handle afppasswd file. we need to make sure that we're root
291    * when we do this. */
292   if (uid)
293     seteuid(0);
294   i = afppasswd(pwd, file, i, passwd, len, set);
295   if (uid)
296     seteuid(uid);
297   return i;
298 }
299
300 /* randnum sends an 8-byte number and uses the user's password to
301  * check against the encrypted reply. */
302 static int rand_login(void *obj, char *username, int ulen, struct passwd **uam_pwd _U_,
303                         char *ibuf _U_, size_t ibuflen _U_,
304                         char *rbuf, size_t *rbuflen)
305 {
306
307   char *passwdfile;
308   u_int16_t sessid;
309   size_t len;
310   int err;
311  
312   if (( randpwd = uam_getname(obj, username, ulen)) == NULL )
313     return AFPERR_PARAM; /* unknown user */
314   
315   LOG(log_info, logtype_uams, "randnum/rand2num login: %s", username);
316   if (uam_checkuser(randpwd) < 0)
317     return AFPERR_NOTAUTH;
318
319   len = UAM_PASSWD_FILENAME;
320   if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT,
321                              (void *) &passwdfile, &len) < 0)
322     return AFPERR_PARAM;
323
324   if ((err = randpass(randpwd, passwdfile, seskey,
325                       sizeof(seskey), 0)) != AFP_OK)
326     return err;
327
328   /* get a random number */
329   len = sizeof(randbuf);
330   if (uam_afpserver_option(obj, UAM_OPTION_RANDNUM,
331                            (void *) randbuf, &len) < 0)
332     return AFPERR_PARAM;
333
334   /* session id is a hashed version of the obj pointer */
335   sessid = randhash(obj);
336   memcpy(rbuf, &sessid, sizeof(sessid));
337   rbuf += sizeof(sessid);
338   *rbuflen = sizeof(sessid);
339   
340   /* send the random number off */
341   memcpy(rbuf, randbuf, sizeof(randbuf));
342   *rbuflen += sizeof(randbuf);
343   return AFPERR_AUTHCONT;
344 }
345
346
347 /* check encrypted reply. we actually setup the encryption stuff
348  * here as the first part of randnum and rand2num are identical. */
349 static int randnum_logincont(void *obj, struct passwd **uam_pwd,
350                              char *ibuf, size_t ibuflen _U_, 
351                              char *rbuf _U_, size_t *rbuflen)
352 {
353   u_int16_t sessid;
354
355   *rbuflen = 0;
356
357   memcpy(&sessid, ibuf, sizeof(sessid));
358   if (sessid != randhash(obj))
359     return AFPERR_PARAM;
360
361   ibuf += sizeof(sessid);
362
363   /* encrypt. this saves a little space by using the fact that
364    * des can encrypt in-place without side-effects. */
365   key_sched((C_Block *) seskey, seskeysched);
366   memset(seskey, 0, sizeof(seskey));
367   ecb_encrypt((C_Block *) randbuf, (C_Block *) randbuf,
368                seskeysched, DES_ENCRYPT);
369   memset(&seskeysched, 0, sizeof(seskeysched));
370
371   /* test against what the client sent */
372   if (memcmp( randbuf, ibuf, sizeof(randbuf) )) { /* != */
373     memset(randbuf, 0, sizeof(randbuf));
374     return AFPERR_NOTAUTH;
375   }
376
377   memset(randbuf, 0, sizeof(randbuf));
378   *uam_pwd = randpwd;
379   return AFP_OK;
380 }
381
382
383 /* differences from randnum:
384  * 1) each byte of the key is shifted left one bit
385  * 2) client sends the server a 64-bit number. the server encrypts it
386  *    and sends it back as part of the reply.
387  */
388 static int rand2num_logincont(void *obj, struct passwd **uam_pwd,
389                               char *ibuf, size_t ibuflen _U_, 
390                               char *rbuf, size_t *rbuflen)
391 {
392   u_int16_t sessid;
393   unsigned int i;
394
395   *rbuflen = 0;
396
397   /* compare session id */
398   memcpy(&sessid, ibuf, sizeof(sessid));
399   if (sessid != randhash(obj))
400     return AFPERR_PARAM;
401
402   ibuf += sizeof(sessid);
403
404   /* shift key elements left one bit */
405   for (i = 0; i < sizeof(seskey); i++)
406     seskey[i] <<= 1;
407
408   /* encrypt randbuf */
409   key_sched((C_Block *) seskey, seskeysched);
410   memset(seskey, 0, sizeof(seskey));
411   ecb_encrypt( (C_Block *) randbuf, (C_Block *) randbuf,
412                seskeysched, DES_ENCRYPT);
413
414   /* test against client's reply */
415   if (memcmp(randbuf, ibuf, sizeof(randbuf))) { /* != */
416     memset(randbuf, 0, sizeof(randbuf));
417     memset(&seskeysched, 0, sizeof(seskeysched));
418     return AFPERR_NOTAUTH;
419   }
420   ibuf += sizeof(randbuf);
421   memset(randbuf, 0, sizeof(randbuf));
422
423   /* encrypt client's challenge and send back */
424   ecb_encrypt( (C_Block *) ibuf, (C_Block *) rbuf,
425                seskeysched, DES_ENCRYPT);
426   memset(&seskeysched, 0, sizeof(seskeysched));
427   *rbuflen = sizeof(randbuf);
428   
429   *uam_pwd = randpwd;
430   return AFP_OK;
431 }
432
433 /* change password  --
434  * NOTE: an FPLogin must already have completed successfully for this
435  *       to work. 
436  */
437 static int randnum_changepw(void *obj, const char *username _U_, 
438                             struct passwd *pwd, char *ibuf,
439                             size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen _U_)
440 {
441     char *passwdfile;
442     int err;
443     size_t len;
444
445     if (uam_checkuser(pwd) < 0)
446       return AFPERR_ACCESS;
447
448     len = UAM_PASSWD_FILENAME;
449     if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT, 
450                              (void *) &passwdfile, &len) < 0)
451       return AFPERR_PARAM;
452
453     /* old password is encrypted with new password and new password is
454      * encrypted with old. */
455     if ((err = randpass(pwd, passwdfile, seskey, 
456                         sizeof(seskey), 0)) != AFP_OK)
457       return err;
458
459     /* use old passwd to decrypt new passwd */
460     key_sched((C_Block *) seskey, seskeysched);
461     ibuf += PASSWDLEN; /* new passwd */
462     ibuf[PASSWDLEN] = '\0';
463     ecb_encrypt( (C_Block *) ibuf, (C_Block *) ibuf, seskeysched, DES_DECRYPT);
464
465     /* now use new passwd to decrypt old passwd */
466     key_sched((C_Block *) ibuf, seskeysched);
467     ibuf -= PASSWDLEN; /* old passwd */
468     ecb_encrypt((C_Block *) ibuf, (C_Block *) ibuf, seskeysched, DES_DECRYPT);
469     if (memcmp(seskey, ibuf, sizeof(seskey))) 
470         err = AFPERR_NOTAUTH;
471     else if (memcmp(seskey, ibuf + PASSWDLEN, sizeof(seskey)) == 0)
472         err = AFPERR_PWDSAME;
473 #ifdef USE_CRACKLIB
474     else if (FascistCheck(ibuf + PASSWDLEN, _PATH_CRACKLIB))
475         err = AFPERR_PWDPOLCY;
476 #endif /* USE_CRACKLIB */
477
478     if (!err) 
479       err = randpass(pwd, passwdfile, ibuf + PASSWDLEN, sizeof(seskey), 1);
480
481     /* zero out some fields */
482     memset(&seskeysched, 0, sizeof(seskeysched));
483     memset(seskey, 0, sizeof(seskey));
484     memset(ibuf, 0, sizeof(seskey)); /* old passwd */
485     memset(ibuf + PASSWDLEN, 0, sizeof(seskey)); /* new passwd */
486
487     if (err)
488       return err;
489
490   return( AFP_OK );
491 }
492
493 /* randnum login */
494 static int randnum_login(void *obj, struct passwd **uam_pwd,
495                         char *ibuf, size_t ibuflen,
496                         char *rbuf, size_t *rbuflen)
497 {
498     char *username;
499     size_t len, ulen;
500
501     *rbuflen = 0;
502
503     if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
504                              (void *) &username, &ulen) < 0)
505         return AFPERR_MISC;
506
507     if (ibuflen < 2) {
508         return( AFPERR_PARAM );
509     }
510
511     len = (unsigned char) *ibuf++;
512     ibuflen--;
513     if (!len || len > ibuflen || len > ulen ) {
514         return( AFPERR_PARAM );
515     }
516     memcpy(username, ibuf, len );
517     ibuf += len;
518     ibuflen -=len;
519     username[ len ] = '\0';
520
521     if ((unsigned long) ibuf & 1) { /* pad character */
522         ++ibuf;
523         ibuflen--;
524     }
525     return (rand_login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
526 }
527
528 /* randnum login ext */
529 static int randnum_login_ext(void *obj, char *uname, struct passwd **uam_pwd,
530                         char *ibuf, size_t ibuflen,
531                         char *rbuf, size_t *rbuflen)
532 {
533     char       *username;
534     size_t     len, ulen;
535     u_int16_t  temp16;
536
537     *rbuflen = 0;
538
539     if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
540                              (void *) &username, &ulen) < 0)
541         return AFPERR_MISC;
542
543     if (*uname != 3)
544         return AFPERR_PARAM;
545     uname++;
546     memcpy(&temp16, uname, sizeof(temp16));
547     len = ntohs(temp16);
548     if (!len || len > ulen ) {
549         return( AFPERR_PARAM );
550     }
551     memcpy(username, uname +2, len );
552     username[ len ] = '\0';
553     return (rand_login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
554 }
555
556 static int uam_setup(const char *path)
557 {
558   if (uam_register(UAM_SERVER_LOGIN_EXT, path, "Randnum exchange", 
559                    randnum_login, randnum_logincont, NULL, randnum_login_ext) < 0)
560     return -1;
561
562   if (uam_register(UAM_SERVER_LOGIN_EXT, path, "2-Way Randnum exchange",
563                    randnum_login, rand2num_logincont, NULL, randnum_login_ext) < 0) {
564     uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
565     return -1;
566   }
567
568   if (uam_register(UAM_SERVER_CHANGEPW, path, "Randnum Exchange", 
569                    randnum_changepw) < 0) {
570     uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
571     uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
572     return -1;
573   }
574   /*uam_register(UAM_SERVER_PRINTAUTH, path, "Randnum Exchange",
575     pam_printer);*/
576
577   return 0;
578 }
579
580 static void uam_cleanup(void)
581 {
582   uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
583   uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
584   uam_unregister(UAM_SERVER_CHANGEPW, "Randnum Exchange");
585   /*uam_unregister(UAM_SERVER_PRINTAUTH, "Randnum Exchange");*/
586 }
587
588 UAM_MODULE_EXPORT struct uam_export uams_randnum = {
589   UAM_MODULE_SERVER,
590   UAM_MODULE_VERSION,
591   uam_setup, uam_cleanup
592 };