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