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