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