]> arthur.barton.de Git - netatalk.git/blob - bin/afppasswd/afppasswd.c
AC_HEADER_STDC autoconf change
[netatalk.git] / bin / afppasswd / afppasswd.c
1 /* 
2  * $Id: afppasswd.c,v 1.8 2001-09-06 20:00:59 rufustfirefly 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 /* STDC check */
28 #if STDC_HEADERS
29 #include <string.h>
30 #else /* STDC_HEADERS */
31 #ifndef HAVE_STRCHR
32 #define strchr index
33 #define strrchr index
34 #endif /* HAVE_STRCHR */
35 char *strchr (), *strrchr ();
36 #ifndef HAVE_MEMCPY
37 #define memcpy(d,s,n) bcopy ((s), (d), (n))
38 #define memmove(d,s,n) bcopy ((s), (d), (n))
39 #endif /* ! HAVE_MEMCPY */
40 #endif /* STDC_HEADERS */
41
42 #include <stdio.h>
43 #include <stdlib.h>
44 #ifdef HAVE_UNISTD_H
45 #include <unistd.h>
46 #endif /* HAVE_UNISTD_H */
47 #include <ctype.h>
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #include <sys/param.h>
51 #ifdef HAVE_FCNTL_H
52 #include <fcntl.h>
53 #endif /* HAVE_FCNTL_H */
54 #include <pwd.h>
55
56 #include <netatalk/endian.h>
57
58 #ifdef UAM_RNDNUM
59 #include <des.h>
60
61 #ifdef USE_CRACKLIB
62 #include <crack.h>
63 #endif /* USE_CRACKLIB */
64
65 #define OPT_ISROOT  (1 << 0)
66 #define OPT_CREATE  (1 << 1)
67 #define OPT_FORCE   (1 << 2)
68 #define OPT_ADDUSER (1 << 3)
69 #define OPT_NOCRACK (1 << 4)
70
71 #define PASSWD_ILLEGAL '*'
72
73 #define FORMAT  ":****************:****************:********\n"
74 #define FORMAT_LEN 44
75 #define OPTIONS "cafnu:p:"
76 #define UID_START 100
77
78 #define HEXPASSWDLEN 16
79 #define PASSWDLEN 8
80
81 static char buf[MAXPATHLEN + 1];
82
83 /* if newpwd is null, convert buf from hex to binary. if newpwd isn't
84  * null, convert newpwd to hex and save it in buf. */
85 #define unhex(x)  (isdigit(x) ? (x) - '0' : toupper(x) + 10 - 'A')
86 static void convert_passwd(char *buf, char *newpwd, const int keyfd)
87 {
88   u_int8_t key[HEXPASSWDLEN];
89   Key_schedule schedule;
90   int i, j;
91
92   if (!newpwd) {
93     /* convert to binary */
94     for (i = j = 0; i < sizeof(key); i += 2, j++)
95       buf[j] = (unhex(buf[i]) << 4) | unhex(buf[i + 1]);
96     if (j <= DES_KEY_SZ)
97       memset(buf + j, 0, sizeof(key) - j);
98   }
99
100   if (keyfd > -1) {
101     lseek(keyfd, 0, SEEK_SET);
102     read(keyfd, key, sizeof(key));
103     /* convert to binary */
104     for (i = j = 0; i < sizeof(key); i += 2, j++)
105       key[j] = (unhex(key[i]) << 4) | unhex(key[i + 1]);
106     if (j <= DES_KEY_SZ)
107       memset(key + j, 0, sizeof(key) - j);
108     key_sched((C_Block *) key, schedule);
109     memset(key, 0, sizeof(key));   
110     if (newpwd) {
111         ecb_encrypt((C_Block *) newpwd, (C_Block *) newpwd, schedule,
112                     DES_ENCRYPT);
113     } else {
114       /* decrypt the password */
115       ecb_encrypt((C_Block *) buf, (C_Block *) buf, schedule, DES_DECRYPT);
116     }
117     memset(schedule, 0, sizeof(schedule));      
118   }
119
120   if (newpwd) {
121     const unsigned char hextable[] = "0123456789ABCDEF";
122
123     /* convert to hex */
124     for (i = j = 0; i < DES_KEY_SZ; i++, j += 2) {
125       buf[j] = hextable[(newpwd[i] & 0xF0) >> 4];
126       buf[j + 1] = hextable[newpwd[i] & 0x0F];
127     }
128   }
129 }
130
131 /* this matches the code in uam_randnum.c */
132 static int update_passwd(const char *path, const char *name, int flags)
133 {
134   char password[PASSWDLEN + 1], *p, *passwd;
135   FILE *fp;
136   off_t pos;
137   int keyfd = -1, err = 0;
138
139   if ((fp = fopen(path, "r+")) == NULL) {
140     fprintf(stderr, "afppasswd: can't open %s\n", path);
141     return -1;
142   }
143
144   /* open the key file if it exists */
145   strcpy(buf, path);
146   if (strlen(path) < sizeof(buf) - 5) {
147     strcat(buf, ".key");
148     keyfd = open(buf, O_RDONLY);
149   } 
150
151   pos = ftell(fp);
152   memset(buf, 0, sizeof(buf));
153   while (fgets(buf, sizeof(buf), fp)) {
154     if ((p = strchr(buf, ':'))) {
155       /* check for a match */
156       if (strncmp(buf, name, p - buf) == 0) {
157         p++;
158         if (!(flags & OPT_ISROOT) && (*p == PASSWD_ILLEGAL)) {
159           fprintf(stderr, "Your password is disabled. Please see your administrator.\n");
160           break;
161         }
162         goto found_entry;
163       }
164     }
165     pos = ftell(fp);
166     memset(buf, 0, sizeof(buf));
167   }
168
169   if (flags & OPT_ADDUSER) {
170     strcpy(buf, name);
171     strcat(buf, FORMAT);
172     p = strchr(buf, ':') + 1;
173     fwrite(buf, strlen(buf), 1, fp);
174   } else {
175     fprintf(stderr, "afppasswd: can't find %s in %s\n", name, path);
176     err = -1;
177     goto update_done;
178   }
179
180 found_entry:
181   /* need to verify against old password */
182   if ((flags & OPT_ISROOT) == 0) {
183     passwd = getpass("Enter OLD AFP password: ");
184     convert_passwd(p, NULL, keyfd);
185     if (strncmp(passwd, p, PASSWDLEN)) {
186       fprintf(stderr, "afppasswd: invalid password.\n");
187       err = -1;
188       goto update_done;
189     }
190   }
191
192   /* new password */
193   passwd = getpass("Enter NEW AFP password: ");
194   memcpy(password, passwd, sizeof(password));
195   password[PASSWDLEN] = '\0';
196 #ifdef USE_CRACKLIB
197   if (!(flags & OPT_NOCRACK)) {
198     if (passwd = FascistCheck(password, _PATH_CRACKLIB)) { 
199         fprintf(stderr, "Error: %s\n", passwd);
200         err = -1;
201         goto update_done;
202     } 
203   }
204 #endif /* USE_CRACKLIB */
205
206   passwd = getpass("Enter NEW AFP password again: ");
207   if (strcmp(passwd, password) == 0) {
208      struct flock lock;
209      int fd = fileno(fp);
210
211      convert_passwd(p, password, keyfd);
212      lock.l_type = F_WRLCK;
213      lock.l_start = pos;
214      lock.l_len = 1;
215      lock.l_whence = SEEK_SET;
216      fseek(fp, pos, SEEK_SET);
217      fcntl(fd, F_SETLKW, &lock);
218      fwrite(buf, p - buf + HEXPASSWDLEN, 1, fp);
219      lock.l_type = F_UNLCK;
220      fcntl(fd, F_SETLK, &lock);
221      printf("afppasswd: updated password.\n");
222
223   } else {
224     fprintf(stderr, "afppasswd: passwords don't match!\n");
225     err = -1;
226   }
227
228 update_done:
229   if (keyfd > -1)
230     close(keyfd);
231   fclose(fp);
232   return err;
233 }
234
235
236 /* creates a file with all the password entries */
237 static int create_file(const char *path, uid_t minuid)
238 {
239   struct passwd *pwd;
240   int fd, len, err = 0;
241
242   
243   if ((fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0600)) < 0) {
244     fprintf(stderr, "afppasswd: can't create %s\n", path);
245     return -1;
246   }
247
248   setpwent();
249   while ((pwd = getpwent())) {
250     if (pwd->pw_uid < minuid)
251       continue;
252     /* a little paranoia */
253     if (strlen(pwd->pw_name) + FORMAT_LEN > sizeof(buf) - 1)
254       continue;
255     strcpy(buf, pwd->pw_name);
256     strcat(buf, FORMAT);
257     len = strlen(buf);
258     if (write(fd, buf, len) != len) {
259       fprintf(stderr, "afppasswd: problem writing to %s: %m\n", path);
260       err = -1;
261       break;
262     }
263   }
264   endpwent();
265   close(fd);
266
267   return err;
268 }
269
270
271 int main(int argc, char **argv)
272 {
273   struct stat st;
274   int flags;
275   uid_t uid_min = UID_START, uid;
276   char *path = _PATH_AFPDPWFILE;
277   int i, err = 0;
278
279   extern char *optarg;
280   extern int optind;
281
282   flags = ((uid = getuid()) == 0) ? OPT_ISROOT : 0;
283
284   if (((flags & OPT_ISROOT) == 0) && (argc > 1)) {
285     fprintf(stderr, "Usage: afppasswd [-acfn] [-u minuid] [-p path] [username]\n");
286     fprintf(stderr, "  -a        add a new user\n");
287     fprintf(stderr, "  -c        create and initialize password file or specific user\n");
288     fprintf(stderr, "  -f        force an action\n");
289 #ifdef USE_CRACKLIB
290     fprintf(stderr, "  -n        disable cracklib checking of passwords\n");
291 #endif /* USE_CRACKLIB */
292     fprintf(stderr, "  -u uid    minimum uid to use, defaults to 100\n");
293     fprintf(stderr, "  -p path   path to afppasswd file\n");
294     return -1;
295   }
296
297   while ((i = getopt(argc, argv, OPTIONS)) != EOF) {
298     switch (i) {
299     case 'c': /* create and initialize password file or specific user */
300       flags |= OPT_CREATE;
301       break;
302     case 'a': /* add a new user */
303       flags |= OPT_ADDUSER;
304       break;
305     case 'f': /* force an action */
306       flags |= OPT_FORCE;
307       break;
308     case 'u':  /* minimum uid to use. default is 100 */
309       uid_min = atoi(optarg);
310       break;
311 #ifdef USE_CRACKLIB
312     case 'n': /* disable CRACKLIB check */
313       flags |= OPT_NOCRACK;
314       break;
315 #endif /* USE_CRACKLIB */
316     case 'p': /* path to afppasswd file */
317       path = optarg;
318       break;
319     default:
320       err++;
321       break;
322     }
323   }
324   
325   if (err || (optind + ((flags & OPT_CREATE) ? 0 : 
326                         (flags & OPT_ISROOT)) != argc)) {
327 #ifdef USE_CRACKLIB
328     fprintf(stderr, "Usage: afppasswd [-acfn] [-u minuid] [-p path] [username]\n");
329 #else /* USE_CRACKLIB */
330     fprintf(stderr, "Usage: afppasswd [-acf] [-u minuid] [-p path] [username]\n");
331 #endif /* USE_CRACKLIB */
332     fprintf(stderr, "  -a        add a new user\n");
333     fprintf(stderr, "  -c        create and initialize password file or specific user\n");
334     fprintf(stderr, "  -f        force an action\n");
335 #ifdef USE_CRACKLIB
336     fprintf(stderr, "  -n        disable cracklib checking of passwords\n");
337 #endif /* USE_CRACKLIB */
338     fprintf(stderr, "  -u uid    minimum uid to use, defaults to 100\n");
339     fprintf(stderr, "  -p path   path to afppasswd file\n");
340     return -1;
341   }
342
343   i = stat(path, &st);
344   if (flags & OPT_CREATE) {
345     if ((flags & OPT_ISROOT) == 0) {
346       fprintf(stderr, "afppasswd: only root can create the password file.\n");
347       return -1;
348     }
349
350     if (!i && ((flags & OPT_FORCE) == 0)) {
351       fprintf(stderr, "afppasswd: password file already exists.\n");
352       return -1;
353     }
354     return create_file(path, uid_min);
355
356   } else {
357     struct passwd *pwd = NULL;
358
359     if (i < 0) {
360       fprintf(stderr, "afppasswd: %s doesn't exist.\n", path);
361       return -1;
362     }
363     
364     /* if we're root, we need to specify the username */
365     pwd = (flags & OPT_ISROOT) ? getpwnam(argv[optind]) : getpwuid(uid);
366     if (pwd)
367       return update_passwd(path, pwd->pw_name, flags);
368
369     fprintf(stderr, "afppasswd: can't get password entry.\n");
370     return -1;
371   }
372 }
373 #else /* UAM_RNDNUM */
374
375 main(int argc, char **argv)
376 {
377   fprintf(stderr, "afppasswd is only useful if you're using centralized passwords\n");
378   fprintf(stderr, "for the Random Number authentication methods.\n");
379   return -1;
380 }
381
382 #endif /* UAM_RNDNUM */