2 * Copyright 1999 (c) Adrian Sun (asun@u.washington.edu)
3 * All Rights Reserved. See COPYRIGHT.
5 * format of the password file:
6 * name:****************:****************:********
7 * password last login date failed usage count
9 * ***'s are illegal. they're just place holders for hex values. hex
10 * values that represent actual numbers are in network byte order.
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.
15 * the last two fields aren't currently used by the randnum uams.
17 * root syntax: afppasswd [-c] [-a] [-p path] [-f] [username]
18 * user syntax: afppasswd
23 #endif /* HAVE_CONFIG_H */
31 #endif /* HAVE_UNISTD_H */
33 #include <sys/types.h>
35 #include <sys/param.h>
38 #endif /* HAVE_FCNTL_H */
41 #include <netatalk/endian.h>
47 #endif /* USE_CRACKLIB */
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)
55 #define PASSWD_ILLEGAL '*'
57 #define FORMAT ":****************:****************:********\n"
59 #define OPTIONS "cafnu:p:"
62 #define HEXPASSWDLEN 16
65 static char buf[MAXPATHLEN + 1];
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)
72 u_int8_t key[HEXPASSWDLEN];
73 Key_schedule schedule;
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]);
81 memset(buf + j, 0, sizeof(key) - j);
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]);
91 memset(key + j, 0, sizeof(key) - j);
92 key_sched((C_Block *) key, schedule);
93 memset(key, 0, sizeof(key));
95 ecb_encrypt((C_Block *) newpwd, (C_Block *) newpwd, schedule,
98 /* decrypt the password */
99 ecb_encrypt((C_Block *) buf, (C_Block *) buf, schedule, DES_DECRYPT);
101 memset(&schedule, 0, sizeof(schedule));
105 const unsigned char hextable[] = "0123456789ABCDEF";
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];
115 /* this matches the code in uam_randnum.c */
116 static int update_passwd(const char *path, const char *name, int flags)
118 char password[PASSWDLEN + 1], *p, *passwd;
121 int keyfd = -1, err = 0;
123 if ((fp = fopen(path, "r+")) == NULL) {
124 fprintf(stderr, "afppasswd: can't open %s\n", path);
128 /* open the key file if it exists */
130 if (strlen(path) < sizeof(buf) - 5) {
132 keyfd = open(buf, O_RDONLY);
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) {
143 if (!(flags & OPT_ISROOT) && (*p == PASSWD_ILLEGAL)) {
144 fprintf(stderr, "Your password is disabled. Please see your administrator.\n");
151 memset(buf, 0, sizeof(buf));
154 if (flags & OPT_ADDUSER) {
157 p = strchr(buf, ':') + 1;
158 fwrite(buf, strlen(buf), 1, fp);
160 fprintf(stderr, "afppasswd: can't find %s in %s\n", name, path);
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");
178 passwd = getpass("Enter NEW AFP password: ");
179 memcpy(password, passwd, sizeof(password));
180 password[PASSWDLEN] = '\0';
182 if (!(flags & OPT_NOCRACK)) {
183 if (passwd = FascistCheck(password, _PATH_CRACKLIB)) {
184 fprintf(stderr, "Error: %s\n", passwd);
189 #endif /* USE_CRACKLIB */
191 passwd = getpass("Enter NEW AFP password again: ");
192 if (strcmp(passwd, password) == 0) {
196 convert_passwd(p, password, keyfd);
197 lock.l_type = F_WRLCK;
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");
209 fprintf(stderr, "afppasswd: passwords don't match!\n");
221 /* creates a file with all the password entries */
222 static int create_file(const char *path, uid_t minuid)
225 int fd, len, err = 0;
228 if ((fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0600)) < 0) {
229 fprintf(stderr, "afppasswd: can't create %s\n", path);
234 while ((pwd = getpwent())) {
235 if (pwd->pw_uid < minuid)
237 /* a little paranoia */
238 if (strlen(pwd->pw_name) + FORMAT_LEN > sizeof(buf) - 1)
240 strcpy(buf, pwd->pw_name);
243 if (write(fd, buf, len) != len) {
244 fprintf(stderr, "afppasswd: problem writing to %s: %s\n", path,
257 int main(int argc, char **argv)
261 uid_t uid_min = UID_START, uid;
262 char *path = _PATH_AFPDPWFILE;
268 flags = ((uid = getuid()) == 0) ? OPT_ISROOT : 0;
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");
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");
284 while ((i = getopt(argc, argv, OPTIONS)) != EOF) {
286 case 'c': /* create and initialize password file or specific user */
289 case 'a': /* add a new user */
290 flags |= OPT_ADDUSER;
292 case 'f': /* force an action */
295 case 'u': /* minimum uid to use. default is 100 */
296 uid_min = atoi(optarg);
299 case 'n': /* disable CRACKLIB check */
300 flags |= OPT_NOCRACK;
302 #endif /* USE_CRACKLIB */
303 case 'p': /* path to afppasswd file */
312 if (err || (optind + ((flags & OPT_CREATE) ? 0 :
313 (flags & OPT_ISROOT)) != argc)) {
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");
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");
331 if (flags & OPT_CREATE) {
332 if ((flags & OPT_ISROOT) == 0) {
333 fprintf(stderr, "afppasswd: only root can create the password file.\n");
337 if (!i && ((flags & OPT_FORCE) == 0)) {
338 fprintf(stderr, "afppasswd: password file already exists.\n");
341 return create_file(path, uid_min);
344 struct passwd *pwd = NULL;
347 fprintf(stderr, "afppasswd: %s doesn't exist.\n", path);
351 /* if we're root, we need to specify the username */
352 pwd = (flags & OPT_ISROOT) ? getpwnam(argv[optind]) : getpwuid(uid);
354 return update_passwd(path, pwd->pw_name, flags);
356 fprintf(stderr, "afppasswd: can't get password entry.\n");