]> arthur.barton.de Git - netatalk.git/blob - bin/afppasswd/afppasswd.c
Include path fixes.
[netatalk.git] / bin / afppasswd / afppasswd.c
1 /* 
2  * $Id: afppasswd.c,v 1.18 2003-06-09 02:55:25 srittau Exp $
3  *
4  * Copyright 1999 (c) Adrian Sun (asun@u.washington.edu)
5  * All Rights Reserved. See COPYRIGHT.
6  *
7  * format of the password file:
8  * name:****************:****************:********
9  *      password         last login date  failed usage count
10  *
11  * ***'s are illegal. they're just place holders for hex values. hex
12  * values that represent actual numbers are in network byte order.
13  *
14  * last login date is currently a 4-byte number representing seconds
15  * since 1970 (UTC). there's enough space to extend it to 8 bytes.
16  *
17  * the last two fields aren't currently used by the randnum uams.
18  *
19  * root syntax: afppasswd [-c] [-a] [-p path] [-f] [username]
20  * user syntax: afppasswd 
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif /* HAVE_CONFIG_H */
26
27 #include <assert.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <errno.h>
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif /* HAVE_UNISTD_H */
35 #include <ctype.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/param.h>
39 #ifdef HAVE_FCNTL_H
40 #include <fcntl.h>
41 #endif /* HAVE_FCNTL_H */
42 #include <pwd.h>
43
44 #include <netatalk/endian.h>
45
46 #if HAVE_GCRYPT
47 #include <gcrypt.h>
48 #define DES_KEY_SZ 8
49 #else
50 #include <des.h>
51 #endif
52
53 #ifdef USE_CRACKLIB
54 #include <crack.h>
55 #endif /* USE_CRACKLIB */
56
57 #define OPT_ISROOT  (1 << 0)
58 #define OPT_CREATE  (1 << 1)
59 #define OPT_FORCE   (1 << 2)
60 #define OPT_ADDUSER (1 << 3)
61 #define OPT_NOCRACK (1 << 4)
62
63 #define PASSWD_ILLEGAL '*'
64
65 #define FORMAT  ":****************:****************:********\n"
66 #define FORMAT_LEN 44
67 #define OPTIONS "cafnu:p:"
68 #define UID_START 100
69
70 #define PASSWDLEN DES_KEY_SZ
71 #define HEXPASSWDLEN (PASSWDLEN * 2)
72
73 static char buf[MAXPATHLEN + 1];
74
75 /* FIXME: preparation for i18n */
76 #ifndef _
77 #define _(x) (x)
78 #endif
79
80 /* SRC and DST must not overlap. */
81 static void hexify(u_int8_t *dst, const u_int8_t *src)
82 {
83   int i, j;
84
85   for (i = j = 0; i < DES_KEY_SZ; i++, j += 2) {
86     static const unsigned char hextable[] = "0123456789ABCDEF";
87
88     dst[j]     = hextable[(src[i] & 0xF0) >> 4];
89     dst[j + 1] = hextable[(src[i] & 0x0F)];
90   }
91 }
92
93 /* SRC and DST can overlap. */
94 #define unhex(x)  (isdigit(x) ? (x) - '0' : toupper(x) + 10 - 'A')
95 static void unhexify(u_int8_t *dst, const u_int8_t *src)
96 {
97   int i, j;
98
99   /* convert to binary */
100   for (i = j = 0; i < HEXPASSWDLEN; i += 2, j++)
101     dst[j] = (unhex(src[i]) << 4) | unhex(src[i + 1]);
102   if (j <= DES_KEY_SZ)
103     memset(dst + j, 0, HEXPASSWDLEN - j);
104 }
105
106 static u_int8_t *retrieve_key(int keyfd)
107 {
108   static u_int8_t key[HEXPASSWDLEN];
109
110   if (lseek(keyfd, 0, SEEK_SET) == -1)
111     return NULL;
112   if (read(keyfd, key, sizeof(key)) < sizeof(key))
113     return NULL;
114
115   unhexify(key, key);
116
117   return key;
118 }
119
120 static int decrypt_passwd(u_int8_t *dst, const u_int8_t *src, int keyfd)
121 {
122   u_int8_t *key;
123   int err = 0;
124
125   key = retrieve_key(keyfd);
126   if (!key)
127     return 0;
128
129   {
130 #if HAVE_GCRYPT
131     GcryCipherHd handle;
132
133     handle = gcry_cipher_open(GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
134     if (handle) {
135       int ret;
136
137       ret = gcry_cipher_setkey(handle, key, DES_KEY_SZ);
138       if (!ret)
139         ret = gcry_cipher_decrypt(handle, dst, 8, src, 8);
140       gcry_cipher_close(handle);
141       if (ret) {
142         fprintf(stderr, _("Decryption error: %s\n"), gcry_strerror(ret));
143         err = 1;
144       }
145       printf("pw: %s\n", buf); /* FIXME */
146     } else {
147       fprintf(stderr, _("Could not create crypt handle.\n"));
148       err = 1;
149     }
150 #else
151     DES_key_schedule schedule;
152
153     DES_set_key_unchecked((DES_cblock *) key, &schedule);
154     DES_ecb_encrypt((DES_cblock *) src, (DES_cblock *) dst,
155                     &schedule, DES_DECRYPT);
156
157     memset(&schedule, 0, sizeof(schedule));
158 #endif
159   }
160
161   memset(key, 0, HEXPASSWDLEN);
162
163   return err;
164 }
165
166 static int encrypt_passwd(u_int8_t *dst, const u_int8_t *src, int keyfd)
167 {
168   const u_int8_t *key;
169   int err = 0;
170
171   key = retrieve_key(keyfd);
172   if (!key)
173     return 0;
174
175   {
176 #if HAVE_GCRYPT
177     GcryCipherHd handle;
178     int ret;
179                                                                                 
180     handle = gcry_cipher_open(GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
181     if (handle) {
182       ret = gcry_cipher_setkey(handle, key, DES_KEY_SZ);
183       if (!ret)
184         ret = gcry_cipher_encrypt(handle, dst, 8, src, 0);
185       gcry_cipher_close(handle);
186       if (ret) {
187         fprintf(stderr, _("Encryption error: %s\n"), gcry_strerror(ret));
188         err = 1;
189       }
190     } else {
191       fprintf(stderr, _("Could not create crypt handle.\n"));
192       err = 1;
193     }
194 #else
195     DES_key_schedule schedule;
196
197     DES_set_key_unchecked((DES_cblock *) key, &schedule);
198     DES_ecb_encrypt((DES_cblock *) src, (DES_cblock *) dst,
199                     &schedule, DES_ENCRYPT);
200
201     memset(&schedule, 0, sizeof(schedule));      
202 #endif
203   }
204
205   return err;
206 }
207
208 /* this matches the code in uam_randnum.c */
209 static int update_passwd(const char *path, const char *name, int flags)
210 {
211   char password[PASSWDLEN + 1], *p, *passwd;
212   FILE *fp;
213   off_t pos;
214   int found = 0;
215   int keyfd = -1, err = 0;
216
217   if ((fp = fopen(path, "r+")) == NULL) {
218     fprintf(stderr, _("Can't open password file %s: %s.\n"), path, strerror(errno));
219     return 1;
220   }
221
222   /* open the key file if it exists */
223   strcpy(buf, path);
224   if (strlen(path) < sizeof(buf) - 5) {
225     strcat(buf, ".key");
226     keyfd = open(buf, O_RDONLY);
227     if (keyfd == -1 && errno != ENOENT) {
228       fprintf(stderr, _("Can't open key file %s: %s\n"), buf, strerror(errno));
229       err = 1;
230       goto update_done;
231     }
232   } 
233
234   pos = ftell(fp);
235   memset(buf, 0, sizeof(buf));
236   while (fgets(buf, sizeof(buf), fp)) {
237     if ((p = strchr(buf, ':'))) {
238       /* check for a match */
239       if (strncmp(buf, name, p - buf) == 0) {
240         p++;
241         if (!(flags & OPT_ISROOT) && (*p == PASSWD_ILLEGAL)) {
242           fprintf(stderr, _("Your password is disabled. Please see your administrator.\n"));
243           break;
244         }
245         found = 1;
246         break;
247       }
248     }
249     pos = ftell(fp);
250     memset(buf, 0, sizeof(buf));
251   }
252
253   if (!found) {
254     if ((flags & OPT_ISROOT) && (flags & OPT_ADDUSER)) {
255       strcpy(buf, name);
256       strcat(buf, FORMAT);
257       p = strchr(buf, ':') + 1;
258       fwrite(buf, strlen(buf), 1, fp);
259     } else {
260       fprintf(stderr, _("Can't find user %s in %s\n"), name, path);
261       err = 1;
262       goto update_done;
263     }
264   }
265
266   /* need to verify against old password */
267   if ((flags & OPT_ISROOT) == 0) {
268     passwd = getpass(_("Enter OLD AFP password: "));
269     unhexify(p, p);
270     err = decrypt_passwd(p, p, keyfd);
271     if (err) {
272       memset(passwd, 0, strlen(passwd));
273       goto update_done;
274     }
275     if (strncmp(passwd, p, PASSWDLEN)) {
276       memset(passwd, 0, strlen(passwd));
277       fprintf(stderr, _("Wrong password.\n"));
278       err = 1;
279       goto update_done;
280     }
281     memset(passwd, 0, strlen(passwd));
282   }
283
284   /* new password */
285   passwd = getpass(_("Enter NEW AFP password: "));
286   memcpy(password, passwd, sizeof(password));
287   memset(passwd, 0, strlen(passwd));
288   password[PASSWDLEN] = '\0';
289 #ifdef USE_CRACKLIB
290   if (!(flags & OPT_NOCRACK)) {
291     char *str;
292     if (str = FascistCheck(password, _PATH_CRACKLIB)) { 
293       memset(password, 0, PASSWDLEN);
294       fprintf(stderr, _("Error: %s\n"), str);
295       err = 1;
296       goto update_done;
297     } 
298   }
299 #endif /* USE_CRACKLIB */
300
301   passwd = getpass(_("Enter NEW AFP password again: "));
302   if (strcmp(passwd, password) == 0) {
303     struct flock lock;
304     int fd = fileno(fp);
305
306     memset(passwd, 0, strlen(passwd));
307
308     err = encrypt_passwd(password, password, keyfd);
309     if (err)
310       goto update_done;
311     hexify(p, password);
312
313     lock.l_type = F_WRLCK;
314     lock.l_start = pos;
315     lock.l_len = 1;
316     lock.l_whence = SEEK_SET;
317     fseek(fp, pos, SEEK_SET);
318     fcntl(fd, F_SETLKW, &lock);
319     fwrite(buf, p - buf + HEXPASSWDLEN, 1, fp);
320     lock.l_type = F_UNLCK;
321     fcntl(fd, F_SETLK, &lock);
322     printf(_("Updated password.\n"));
323     memset(password, 0, PASSWDLEN);
324
325   } else {
326     memset(passwd, 0, strlen(passwd));
327     memset(password, 0, PASSWDLEN);
328     fprintf(stderr, _("Passwords don't match!\n"));
329     err = 1;
330   }
331
332 update_done:
333   if (keyfd > -1)
334     close(keyfd);
335   fclose(fp);
336   return err;
337 }
338
339
340 /* creates a file with all the password entries */
341 static int create_file(const char *path, uid_t minuid)
342 {
343   struct passwd *pwd;
344   int fd, len, err = 0;
345
346   
347   if ((fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0600)) < 0) {
348     fprintf(stderr, _("Can't create password file %s: %s\n"), path, strerror(errno));
349     return 1;
350   }
351
352   setpwent();
353   while ((pwd = getpwent())) {
354     if (pwd->pw_uid < minuid)
355       continue;
356     /* a little paranoia */
357     if (strlen(pwd->pw_name) + FORMAT_LEN > sizeof(buf) - 1)
358       continue;
359     strcpy(buf, pwd->pw_name);
360     strcat(buf, FORMAT);
361     len = strlen(buf);
362     if (write(fd, buf, len) != len) {
363       fprintf(stderr, _("Problem writing to password file %s: %s\n"),
364               path, strerror(errno));
365       err = 1;
366       break;
367     }
368   }
369   endpwent();
370   close(fd);
371
372   return err;
373 }
374
375
376 static void usage(void)
377 {
378 #ifdef USE_CRACKLIB
379     fprintf(stderr, _("Usage: afppasswd [-acfn] [-u minuid] [-p path] [username]\n"));
380 #else /* USE_CRACKLIB */
381     fprintf(stderr, _("Usage: afppasswd [-acf] [-u minuid] [-p path] [username]\n"));
382 #endif /* USE_CRACKLIB */
383     fprintf(stderr, _("  -a        add a new user\n"));
384     fprintf(stderr, _("  -c        create and initialize password file or specific user\n"));
385     fprintf(stderr, _("  -f        force an action\n"));
386 #ifdef USE_CRACKLIB
387     fprintf(stderr, _("  -n        disable cracklib checking of passwords\n"));
388 #endif /* USE_CRACKLIB */
389     fprintf(stderr, _("  -u uid    minimum uid to use, defaults to 100\n"));
390     fprintf(stderr, _("  -p path   path to afppasswd file\n"));
391 }
392
393
394 int main(int argc, char **argv)
395 {
396   struct stat st;
397   int flags;
398   uid_t uid_min = UID_START, uid;
399   char *path = _PATH_AFPDPWFILE;
400   int i, err = 0;
401
402   extern char *optarg;
403   extern int optind;
404
405   flags = ((uid = getuid()) == 0) ? OPT_ISROOT : 0;
406
407   if (((flags & OPT_ISROOT) == 0) && (argc > 1)) {
408     usage();
409     return 1;
410   }
411
412   while ((i = getopt(argc, argv, OPTIONS)) != EOF) {
413     switch (i) {
414     case 'c': /* create and initialize password file or specific user */
415       flags |= OPT_CREATE;
416       break;
417     case 'a': /* add a new user */
418       flags |= OPT_ADDUSER;
419       break;
420     case 'f': /* force an action */
421       flags |= OPT_FORCE;
422       break;
423     case 'u':  /* minimum uid to use. default is 100 */
424       uid_min = atoi(optarg);
425       break;
426 #ifdef USE_CRACKLIB
427     case 'n': /* disable CRACKLIB check */
428       flags |= OPT_NOCRACK;
429       break;
430 #endif /* USE_CRACKLIB */
431     case 'p': /* path to afppasswd file */
432       path = optarg;
433       break;
434     default:
435       err++;
436       break;
437     }
438   }
439   
440   if (err || (optind + ((flags & OPT_CREATE) ? 0 : 
441                         (flags & OPT_ISROOT)) != argc)) {
442     usage();
443     return 1;
444   }
445
446   if (flags & OPT_CREATE) {
447     if ((flags & OPT_ISROOT) == 0) {
448       fprintf(stderr, _("Only root can create the password file.\n"));
449       return 1;
450     }
451
452     if (!stat(path, &st) && ((flags & OPT_FORCE) == 0)) {
453       fprintf(stderr, _("Password file already exists.\n"));
454       return 1;
455     }
456     return create_file(path, uid_min);
457
458   } else {
459     struct passwd *pwd = NULL;
460
461     if (stat(path, &st) < 0) {
462       fprintf(stderr, _("Password file %s doesn't exist.\n"), path);
463       return 1;
464     }
465     
466     /* if we're root, we need to specify the username */
467     pwd = (flags & OPT_ISROOT) ? getpwnam(argv[optind]) : getpwuid(uid);
468     if (pwd)
469       return update_passwd(path, pwd->pw_name, flags);
470
471     fprintf(stderr, _("Can't get password entry.\n"));
472     return 1;
473   }
474 }