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