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