]> arthur.barton.de Git - netatalk.git/blob - etc/uams/uams_randnum.c
remove gcc warnings and cleanup inline mess
[netatalk.git] / etc / uams / uams_randnum.c
1 /* 
2  * $Id: uams_randnum.c,v 1.12.6.4.2.2 2008-11-25 15:16:33 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_, int ibuflen _U_,
304                         char *rbuf, int *rbuflen)
305 {
306
307   char *passwdfile;
308   u_int16_t sessid;
309   int len, err;
310  
311   if (( randpwd = uam_getname(obj, username, ulen)) == NULL )
312     return AFPERR_PARAM; /* unknown user */
313   
314   LOG(log_info, logtype_uams, "randnum/rand2num login: %s", username);
315   if (uam_checkuser(randpwd) < 0)
316     return AFPERR_NOTAUTH;
317
318   len = UAM_PASSWD_FILENAME;
319   if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT,
320                              (void *) &passwdfile, &len) < 0)
321     return AFPERR_PARAM;
322
323   if ((err = randpass(randpwd, passwdfile, seskey,
324                       sizeof(seskey), 0)) != AFP_OK)
325     return err;
326
327   /* get a random number */
328   len = sizeof(randbuf);
329   if (uam_afpserver_option(obj, UAM_OPTION_RANDNUM,
330                            (void *) randbuf, &len) < 0)
331     return AFPERR_PARAM;
332
333   /* session id is a hashed version of the obj pointer */
334   sessid = randhash(obj);
335   memcpy(rbuf, &sessid, sizeof(sessid));
336   rbuf += sizeof(sessid);
337   *rbuflen = sizeof(sessid);
338   
339   /* send the random number off */
340   memcpy(rbuf, randbuf, sizeof(randbuf));
341   *rbuflen += sizeof(randbuf);
342   return AFPERR_AUTHCONT;
343 }
344
345
346 /* check encrypted reply. we actually setup the encryption stuff
347  * here as the first part of randnum and rand2num are identical. */
348 static int randnum_logincont(void *obj, struct passwd **uam_pwd,
349                              char *ibuf, int ibuflen _U_, 
350                              char *rbuf _U_, int *rbuflen)
351 {
352   u_int16_t sessid;
353
354   *rbuflen = 0;
355
356   memcpy(&sessid, ibuf, sizeof(sessid));
357   if (sessid != randhash(obj))
358     return AFPERR_PARAM;
359
360   ibuf += sizeof(sessid);
361
362   /* encrypt. this saves a little space by using the fact that
363    * des can encrypt in-place without side-effects. */
364   key_sched((C_Block *) seskey, seskeysched);
365   memset(seskey, 0, sizeof(seskey));
366   ecb_encrypt((C_Block *) randbuf, (C_Block *) randbuf,
367                seskeysched, DES_ENCRYPT);
368   memset(&seskeysched, 0, sizeof(seskeysched));
369
370   /* test against what the client sent */
371   if (memcmp( randbuf, ibuf, sizeof(randbuf) )) { /* != */
372     memset(randbuf, 0, sizeof(randbuf));
373     return AFPERR_NOTAUTH;
374   }
375
376   memset(randbuf, 0, sizeof(randbuf));
377   *uam_pwd = randpwd;
378   return AFP_OK;
379 }
380
381
382 /* differences from randnum:
383  * 1) each byte of the key is shifted left one bit
384  * 2) client sends the server a 64-bit number. the server encrypts it
385  *    and sends it back as part of the reply.
386  */
387 static int rand2num_logincont(void *obj, struct passwd **uam_pwd,
388                               char *ibuf, int ibuflen _U_, 
389                               char *rbuf, int *rbuflen)
390 {
391   u_int16_t sessid;
392   unsigned int i;
393
394   *rbuflen = 0;
395
396   /* compare session id */
397   memcpy(&sessid, ibuf, sizeof(sessid));
398   if (sessid != randhash(obj))
399     return AFPERR_PARAM;
400
401   ibuf += sizeof(sessid);
402
403   /* shift key elements left one bit */
404   for (i = 0; i < sizeof(seskey); i++)
405     seskey[i] <<= 1;
406
407   /* encrypt randbuf */
408   key_sched((C_Block *) seskey, seskeysched);
409   memset(seskey, 0, sizeof(seskey));
410   ecb_encrypt( (C_Block *) randbuf, (C_Block *) randbuf,
411                seskeysched, DES_ENCRYPT);
412
413   /* test against client's reply */
414   if (memcmp(randbuf, ibuf, sizeof(randbuf))) { /* != */
415     memset(randbuf, 0, sizeof(randbuf));
416     memset(&seskeysched, 0, sizeof(seskeysched));
417     return AFPERR_NOTAUTH;
418   }
419   ibuf += sizeof(randbuf);
420   memset(randbuf, 0, sizeof(randbuf));
421
422   /* encrypt client's challenge and send back */
423   ecb_encrypt( (C_Block *) ibuf, (C_Block *) rbuf,
424                seskeysched, DES_ENCRYPT);
425   memset(&seskeysched, 0, sizeof(seskeysched));
426   *rbuflen = sizeof(randbuf);
427   
428   *uam_pwd = randpwd;
429   return AFP_OK;
430 }
431
432 /* change password  --
433  * NOTE: an FPLogin must already have completed successfully for this
434  *       to work. 
435  */
436 static int randnum_changepw(void *obj, const char *username _U_, 
437                             struct passwd *pwd, char *ibuf,
438                             int ibuflen _U_, char *rbuf _U_, int *rbuflen _U_)
439 {
440     char *passwdfile;
441     int err, len;
442
443     if (uam_checkuser(pwd) < 0)
444       return AFPERR_ACCESS;
445
446     len = UAM_PASSWD_FILENAME;
447     if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT, 
448                              (void *) &passwdfile, &len) < 0)
449       return AFPERR_PARAM;
450
451     /* old password is encrypted with new password and new password is
452      * encrypted with old. */
453     if ((err = randpass(pwd, passwdfile, seskey, 
454                         sizeof(seskey), 0)) != AFP_OK)
455       return err;
456
457     /* use old passwd to decrypt new passwd */
458     key_sched((C_Block *) seskey, seskeysched);
459     ibuf += PASSWDLEN; /* new passwd */
460     ibuf[PASSWDLEN] = '\0';
461     ecb_encrypt( (C_Block *) ibuf, (C_Block *) ibuf, seskeysched, DES_DECRYPT);
462
463     /* now use new passwd to decrypt old passwd */
464     key_sched((C_Block *) ibuf, seskeysched);
465     ibuf -= PASSWDLEN; /* old passwd */
466     ecb_encrypt((C_Block *) ibuf, (C_Block *) ibuf, seskeysched, DES_DECRYPT);
467     if (memcmp(seskey, ibuf, sizeof(seskey))) 
468         err = AFPERR_NOTAUTH;
469     else if (memcmp(seskey, ibuf + PASSWDLEN, sizeof(seskey)) == 0)
470         err = AFPERR_PWDSAME;
471 #ifdef USE_CRACKLIB
472     else if (FascistCheck(ibuf + PASSWDLEN, _PATH_CRACKLIB))
473         err = AFPERR_PWDPOLCY;
474 #endif /* USE_CRACKLIB */
475
476     if (!err) 
477       err = randpass(pwd, passwdfile, ibuf + PASSWDLEN, sizeof(seskey), 1);
478
479     /* zero out some fields */
480     memset(&seskeysched, 0, sizeof(seskeysched));
481     memset(seskey, 0, sizeof(seskey));
482     memset(ibuf, 0, sizeof(seskey)); /* old passwd */
483     memset(ibuf + PASSWDLEN, 0, sizeof(seskey)); /* new passwd */
484
485     if (err)
486       return err;
487
488   return( AFP_OK );
489 }
490
491 /* randnum login */
492 static int randnum_login(void *obj, struct passwd **uam_pwd,
493                         char *ibuf, int ibuflen,
494                         char *rbuf, int *rbuflen)
495 {
496     char *username;
497     int len, ulen;
498
499     *rbuflen = 0;
500
501     if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
502                              (void *) &username, &ulen) < 0)
503         return AFPERR_MISC;
504
505     if (ibuflen <= 1) {
506         return( AFPERR_PARAM );
507     }
508
509     len = (unsigned char) *ibuf++;
510     ibuflen--;
511     if (!len || len > ibuflen || len > ulen ) {
512         return( AFPERR_PARAM );
513     }
514     memcpy(username, ibuf, len );
515     ibuf += len;
516     ibuflen -=len;
517     username[ len ] = '\0';
518
519     if ((unsigned long) ibuf & 1) { /* pad character */
520         ++ibuf;
521         ibuflen--;
522     }
523     return (rand_login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
524 }
525
526 /* randnum login ext */
527 static int randnum_login_ext(void *obj, char *uname, struct passwd **uam_pwd,
528                         char *ibuf, int ibuflen,
529                         char *rbuf, int *rbuflen)
530 {
531     char       *username;
532     int        len, ulen;
533     u_int16_t  temp16;
534
535     *rbuflen = 0;
536
537     if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
538                              (void *) &username, &ulen) < 0)
539         return AFPERR_MISC;
540
541     if (*uname != 3)
542         return AFPERR_PARAM;
543     uname++;
544     memcpy(&temp16, uname, sizeof(temp16));
545     len = ntohs(temp16);
546     if (!len || len > ulen ) {
547         return( AFPERR_PARAM );
548     }
549     memcpy(username, uname +2, len );
550     username[ len ] = '\0';
551     return (rand_login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
552 }
553
554 static int uam_setup(const char *path)
555 {
556   if (uam_register(UAM_SERVER_LOGIN_EXT, path, "Randnum exchange", 
557                    randnum_login, randnum_logincont, NULL, randnum_login_ext) < 0)
558     return -1;
559
560   if (uam_register(UAM_SERVER_LOGIN_EXT, path, "2-Way Randnum exchange",
561                    randnum_login, rand2num_logincont, NULL, randnum_login_ext) < 0) {
562     uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
563     return -1;
564   }
565
566   if (uam_register(UAM_SERVER_CHANGEPW, path, "Randnum Exchange", 
567                    randnum_changepw) < 0) {
568     uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
569     uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
570     return -1;
571   }
572   /*uam_register(UAM_SERVER_PRINTAUTH, path, "Randnum Exchange",
573     pam_printer);*/
574
575   return 0;
576 }
577
578 static void uam_cleanup(void)
579 {
580   uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
581   uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
582   uam_unregister(UAM_SERVER_CHANGEPW, "Randnum Exchange");
583   /*uam_unregister(UAM_SERVER_PRINTAUTH, "Randnum Exchange");*/
584 }
585
586 UAM_MODULE_EXPORT struct uam_export uams_randnum = {
587   UAM_MODULE_SERVER,
588   UAM_MODULE_VERSION,
589   uam_setup, uam_cleanup
590 };