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