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