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