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