]> arthur.barton.de Git - netatalk.git/blob - etc/uams/uams_randnum.c
uams CVS Id tag addition, uams_dhx_pam auth module works again, new spec file for...
[netatalk.git] / etc / uams / uams_randnum.c
1 /* 
2  * $Id: uams_randnum.c,v 1.4 2001-02-27 17:07:43 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
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
23 #include <syslog.h>
24
25 #include <netatalk/endian.h>
26
27 #include <atalk/afp.h>
28 #include <atalk/uam.h>
29
30
31 #ifdef UAM_RNDNUM
32 #include <des.h>
33
34 #ifdef USE_CRACKLIB
35 #include <crack.h>
36 #endif
37
38 #ifndef __inline__
39 #define __inline__
40 #endif
41
42 #define PASSWDLEN 8
43
44 static C_Block          seskey;
45 static Key_schedule     seskeysched;
46 static struct passwd    *randpwd;
47 static u_int8_t         randbuf[8];
48
49 /* hash to a 16-bit number. this will generate completely harmless 
50  * warnings on 64-bit machines. */
51 #define randhash(a) (((((unsigned long) a) >> 8) ^ \
52                       ((unsigned long)a)) & 0xffff)
53
54
55 /* handle ~/.passwd. courtesy of shirsch@ibm.net. */
56 static  __inline__ int home_passwd(const struct passwd *pwd, 
57                                    const char *path, const int pathlen, 
58                                    char *passwd, const int len,
59                                    const int set)
60 {
61   struct stat st;
62   int fd, i;
63   
64   if ( (fd = open(path, (set) ? O_WRONLY : O_RDONLY)) < 0 ) {
65     syslog( LOG_ERR, "Failed to open %s", path);
66     return AFPERR_ACCESS;
67   }
68
69   if ( fstat( fd, &st ) < 0 ) 
70     goto home_passwd_fail;
71   
72   /* If any of these are true, disallow login: 
73    * - not a regular file
74    * - gid or uid don't match user
75    * - anyone else has permissions of any sort
76    */
77   if (!S_ISREG(st.st_mode) || (pwd->pw_uid != st.st_uid) ||
78       (pwd->pw_gid != st.st_gid) ||
79       (st.st_mode & ( S_IRWXG | S_IRWXO )) ) {
80     syslog( LOG_INFO, "Insecure permissions found for %s.", path);
81     goto home_passwd_fail;
82   }
83
84   /* get the password */
85   if (set) {
86     if (write(fd, passwd, len) < 0) {
87       syslog( LOG_ERR, "Failed to write to %s", path );
88       goto home_passwd_fail;
89     }
90   } else {
91     if (read(fd, passwd, len) < 0) {
92       syslog( LOG_ERR, "Failed to read from %s", path );
93       goto home_passwd_fail;
94     }
95   
96   /* get rid of pesky characters */
97   for (i = 0; i < len; i++)
98     if ((passwd[i] != ' ') && isspace(passwd[i]))
99       passwd[i] = '\0';
100   }
101
102   close(fd);
103   return AFP_OK;
104
105 home_passwd_fail:
106   close(fd);
107   return AFPERR_ACCESS;
108 }
109
110
111
112 /* 
113  * handle /path/afppasswd with an optional key file. we're a lot more
114  * trusting of this file. NOTE: we use our own password entry writing
115  * bits as we want to avoid tromping over global variables. in addition,
116  * we look for a key file and use that if it's there. here are the 
117  * formats: 
118  * password file:
119  * username:password:last login date:failedcount
120  *
121  * password is just the hex equivalent of either the ASCII password
122  * (if the key file doesn't exist) or the des encrypted password.
123  *
124  * key file: 
125  * key (in hex) */
126 #define PASSWD_ILLEGAL '*'
127 #define unhex(x)  (isdigit(x) ? (x) - '0' : toupper(x) + 10 - 'A')
128 static int afppasswd(const struct passwd *pwd, 
129                      const char *path, const int pathlen, 
130                      char *passwd, int len, 
131                      const int set)
132 {
133   u_int8_t key[DES_KEY_SZ*2];
134   char buf[MAXPATHLEN + 1], *p;
135   Key_schedule  schedule;
136   FILE *fp;
137   int i, j, keyfd = -1, err = 0;
138   off_t pos;
139   
140   if ((fp = fopen(path, (set) ? "r+" : "r")) <= 0) {
141     syslog( LOG_ERR, "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 < 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 (strncmp(buf, pwd->pw_name, p - buf) == 0) {
157         p++;
158         if (*p == PASSWD_ILLEGAL) {
159           syslog(LOG_INFO, "invalid password entry for %s", pwd->pw_name);
160           err = AFPERR_ACCESS;
161           goto afppasswd_done;
162         }
163         goto afppasswd_found;
164       }
165     }
166     pos = ftell(fp);
167     memset(buf, 0, sizeof(buf));
168   }
169   err = AFPERR_PARAM;
170   goto afppasswd_done;
171
172 afppasswd_found:
173   if (!set) {
174     /* convert to binary. */
175     for (i = j = 0; i < sizeof(key); i += 2, j++)
176       p[j] = (unhex(p[i]) << 4) | unhex(p[i + 1]);
177     if (j <= DES_KEY_SZ)
178       memset(p + j, 0, sizeof(key) - j);
179   }
180
181   if (keyfd > -1) {
182       /* read in the hex representation of an 8-byte key */
183       read(keyfd, key, sizeof(key));
184
185       /* convert to binary key */
186       for (i = j = 0; i < strlen(key); i += 2, j++)
187         key[j] = (unhex(key[i]) << 4) | unhex(key[i + 1]);
188       if (j <= DES_KEY_SZ)
189         memset(key + j, 0, sizeof(key) - j);
190       key_sched((C_Block *) key, schedule);
191       memset(key, 0, sizeof(key));
192
193       if (set) {
194         /* NOTE: this takes advantage of the fact that passwd doesn't
195          *       get used after this call if it's being set. */
196         ecb_encrypt((C_Block *) passwd, (C_Block *) passwd, schedule,
197                     DES_ENCRYPT);
198       } else {
199         /* decrypt the password */
200         ecb_encrypt((C_Block *) p, (C_Block *) p, schedule, DES_DECRYPT);
201       }
202       memset(schedule, 0, sizeof(schedule));
203   }
204
205   if (set) {
206     const unsigned char hextable[] = "0123456789ABCDEF";
207     struct flock lock;
208     int fd = fileno(fp);
209
210     /* convert to hex password */
211     for (i = j = 0; i < DES_KEY_SZ; i++, j += 2) {
212       key[j] = hextable[(passwd[i] & 0xF0) >> 4];
213       key[j + 1] = hextable[passwd[i] & 0x0F];
214     }
215     memcpy(p, key, sizeof(key));
216
217     /* get exclusive access to the user's password entry. we don't
218      * worry so much on reads. in the worse possible case there, the 
219      * user will just need to re-enter their password. */
220     lock.l_type = F_WRLCK;
221     lock.l_start = pos;
222     lock.l_len = 1;
223     lock.l_whence = SEEK_SET;
224
225     fseek(fp, pos, SEEK_SET);
226     fcntl(fd, F_SETLKW, &lock);
227     fwrite(buf, p - buf + sizeof(key), 1, fp);
228     lock.l_type = F_UNLCK;
229     fcntl(fd, F_SETLK, &lock);
230   } else 
231     memcpy(passwd, p, len);
232
233   memset(buf, 0, sizeof(buf));
234
235 afppasswd_done:
236   if (keyfd > -1)
237     close(keyfd);
238   fclose(fp);
239   return err;
240 }
241
242
243 /* this sets the uid. it needs to do slightly different things
244  * depending upon whether or not the password is in ~/.passwd
245  * or in a global location */
246 static int randpass(const struct passwd *pwd, const char *file,
247                     char *passwd, const int len, const int set) 
248 {
249   int i;
250   uid_t uid = geteuid();
251
252   /* Build pathname to user's '.passwd' file */
253   i = strlen(file);
254   if (*file == '~') {
255     char path[MAXPATHLEN + 1];
256
257     if ( (strlen(pwd->pw_dir) + i - 1) > MAXPATHLEN)
258     return AFPERR_PARAM;
259   
260     strcpy(path,  pwd->pw_dir );
261     strcat(path, "/" );
262     strcat(path, file + 2);
263     if (!uid)
264       seteuid(pwd->pw_uid); /* change ourselves to the user */
265     i = home_passwd(pwd, path, i, passwd, len, set);
266     if (!uid)
267       seteuid(0); /* change ourselves back to root */
268     return i;
269   } 
270
271   if (i > MAXPATHLEN)
272     return AFPERR_PARAM;
273
274   /* handle afppasswd file. we need to make sure that we're root
275    * when we do this. */
276   if (uid)
277     seteuid(0);
278   i = afppasswd(pwd, file, i, passwd, len, set);
279   if (uid)
280     seteuid(uid);
281   return i;
282 }
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 randnum_login(void *obj, struct passwd **uam_pwd,
288                          char *ibuf, int ibuflen,
289                          char *rbuf, int *rbuflen)
290 {
291   char *username, *passwdfile;
292   u_int16_t sessid;
293   int len, ulen, err;
294   
295   *rbuflen = 0;
296   
297   if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, 
298                            (void *) &username, &ulen) < 0)
299     return AFPERR_PARAM;
300
301   len = UAM_PASSWD_FILENAME;
302   if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT, 
303                              (void *) &passwdfile, &len) < 0)
304     return AFPERR_PARAM;
305
306   len = (unsigned char) *ibuf++;
307   if ( len > ulen ) {
308         return AFPERR_PARAM;
309   }
310   memcpy(username, ibuf, len );
311   ibuf += len;
312   username[ len ] = '\0';
313   if ((unsigned long) ibuf & 1) /* padding */
314     ++ibuf;
315   
316   if (( randpwd = uam_getname(username, ulen)) == NULL )
317     return AFPERR_PARAM; /* unknown user */
318   
319   syslog( LOG_INFO, "randnum/rand2num login: %s", username);
320   if (uam_checkuser(randpwd) < 0)
321     return AFPERR_NOTAUTH;
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, 
350                              char *rbuf, 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, 
389                               char *rbuf, int *rbuflen)
390 {
391   u_int16_t sessid;
392   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, 
437                             struct passwd *pwd, char *ibuf,
438                             int ibuflen, char *rbuf, int *rbuflen)
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
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 static int uam_setup(const char *path)
492 {
493   if (uam_register(UAM_SERVER_LOGIN, path, "Randnum exchange", 
494                    randnum_login, randnum_logincont, NULL) < 0)
495     return -1;
496   if (uam_register(UAM_SERVER_LOGIN, path, "2-Way Randnum exchange",
497                    randnum_login, rand2num_logincont, NULL) < 0) {
498     uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
499     return -1;
500   }
501     
502   if (uam_register(UAM_SERVER_CHANGEPW, path, "Randnum Exchange", 
503                    randnum_changepw) < 0) {
504     uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
505     uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
506     return -1;
507   }
508   /*uam_register(UAM_SERVER_PRINTAUTH, path, "Randnum Exchange",
509     pam_printer);*/
510
511   return 0;
512 }
513
514 static void uam_cleanup(void)
515 {
516   uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
517   uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
518   uam_unregister(UAM_SERVER_CHANGEPW, "Randnum Exchange");
519   /*uam_unregister(UAM_SERVER_PRINTAUTH, "Randnum Exchange");*/
520 }
521
522 UAM_MODULE_EXPORT struct uam_export uams_randnum = {
523   UAM_MODULE_SERVER,
524   UAM_MODULE_VERSION,
525   uam_setup, uam_cleanup
526 };
527
528 #endif /* UAM_RNDNUM */