netatalk  4.6.0
Free and Open Source Apple Filing Protocol (AFP) Server
Loading...
Searching...
No Matches
afppasswd.c
Go to the documentation of this file.
1/*
2 * Copyright 1999 (c) Adrian Sun (asun@u.washington.edu)
3 * Copyright 2026 (c) Daniel Markstedt <daniel@mindani.net>
4 * All Rights Reserved. See COPYRIGHT.
5 */
6
21
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif /* HAVE_CONFIG_H */
25
26#include <arpa/inet.h>
27#include <ctype.h>
28#include <errno.h>
29#include <fcntl.h>
30#include <inttypes.h>
31#include <pwd.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <sys/param.h>
36#include <sys/stat.h>
37#include <sys/types.h>
38#include <unistd.h>
39
40#include <gcrypt.h>
41
42#include <atalk/compat.h>
43#include <atalk/constant_time.h>
44#include <atalk/srp.h>
45#include <atalk/uam.h>
46
47#include "afppasswd_migrate.h"
48
49#ifndef DES_KEY_SZ
50#define DES_KEY_SZ 8
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#define OPT_RANDNUM (1 << 5)
63#define OPT_MIGRATE (1 << 6)
64#define OPT_DISABLE (1 << 7)
65
66#define PASSWD_ILLEGAL '*'
67
68/* RandNum format */
69#define FORMAT ":****************:****************:********\n"
70#define FORMAT_LEN 44
71
72#define SRP_PASSWDLEN 255
73
74#define UID_START 100
75#define HEXPASSWDLEN 16
76#define PASSWDLEN 8
77
78#define AFPPASSWD_OPTSTRING "cd:fmnra:p:u:w:"
79
80static int unhex(unsigned char x)
81{
82 return isdigit(x) ? x - '0' : toupper(x) + 10 - 'A';
83}
84
85static char buf[MAXPATHLEN + 1];
86static const unsigned char hextable[] = "0123456789ABCDEF";
87
88/*
89 * Libgcrypt requires gcry_check_version() before any other API call. Keep
90 * this separate so the standalone unit test, which exercises the internal
91 * SRP helpers without entering this program's main(), can initialize it too.
92 */
93static int initialize_libgcrypt(void)
94{
95 gcry_control(GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_SYSTEM);
96
97 if (!gcry_check_version(UAM_NEED_LIBGCRYPT_VERSION)) {
98 fprintf(stderr, "afppasswd: libgcrypt %s or later is required.\n",
100 return -1;
101 }
102
103 return 0;
104}
105
106static int parse_minimum_uid(const char *value, uid_t *uid)
107{
108 char *end;
109 uintmax_t parsed;
110
111 if (*value == '\0' || *value == '-' ||
112 isspace((unsigned char) * value)) {
113 return -1;
114 }
115
116 errno = 0;
117 parsed = strtoumax(value, &end, 10);
118
119 if (errno == ERANGE || end == value || *end != '\0' ||
120 (uintmax_t)(uid_t)parsed != parsed) {
121 return -1;
122 }
123
124 *uid = (uid_t)parsed;
125 return 0;
126}
127
128static int validate_opened_file(int fd, const char *path)
129{
130 struct stat st;
131
132 if (fstat(fd, &st) < 0) {
133 fprintf(stderr, "afppasswd: can't inspect %s: %s\n", path,
134 strerror(errno));
135 return -1;
136 }
137
138 if (!S_ISREG(st.st_mode)) {
139 fprintf(stderr, "afppasswd: %s is not a regular file.\n", path);
140 return -1;
141 }
142
143 if (st.st_uid != geteuid()) {
144 fprintf(stderr, "afppasswd: %s is not owned by the administrator.\n",
145 path);
146 return -1;
147 }
148
149 if (st.st_mode & (S_IRWXG | S_IRWXO)) {
150 fprintf(stderr,
151 "afppasswd: %s must not be accessible by group or other.\n",
152 path);
153 return -1;
154 }
155
156 if (st.st_nlink != 1) {
157 fprintf(stderr, "afppasswd: %s must have exactly one hard link.\n",
158 path);
159 return -1;
160 }
161
162 return 0;
163}
164
165static int open_credential_file(const char *path, int access_mode)
166{
167 int fd;
168 fd = open(path, access_mode | O_CLOEXEC | O_NOFOLLOW);
169
170 if (fd < 0) {
171 fprintf(stderr, "afppasswd: can't open %s: %s\n", path,
172 strerror(errno));
173 return -1;
174 }
175
176 if (validate_opened_file(fd, path) < 0) {
177 close(fd);
178 return -1;
179 }
180
181 return fd;
182}
183
184static int find_append_position(FILE *fp, const char *path, off_t *pos)
185{
186 int last;
187 clearerr(fp);
188
189 if (fseek(fp, 0, SEEK_END) < 0 || (*pos = ftell(fp)) < 0) {
190 fprintf(stderr, "afppasswd: can't seek %s: %s\n", path,
191 strerror(errno));
192 return -1;
193 }
194
195 if (*pos == 0) {
196 return 0;
197 }
198
199 if (fseek(fp, -1, SEEK_END) < 0 || (last = fgetc(fp)) == EOF) {
200 fprintf(stderr, "afppasswd: can't inspect the end of %s: %s\n", path,
201 strerror(errno));
202 return -1;
203 }
204
205 if (last != '\n') {
206 fprintf(stderr,
207 "afppasswd: can't append to %s: final record is incomplete.\n",
208 path);
209 return -1;
210 }
211
212 return 0;
213}
214
216{
217 int fd = open(path, O_CREAT | O_RDWR | O_CLOEXEC | O_NOFOLLOW, 0600);
218
219 if (fd < 0) {
220 fprintf(stderr, "afppasswd: can't create %s: %s\n", path,
221 strerror(errno));
222 return -1;
223 }
224
225 if (validate_opened_file(fd, path) < 0) {
226 close(fd);
227 return -1;
228 }
229
230 if (fchmod(fd, 0600) < 0 || ftruncate(fd, 0) < 0) {
231 fprintf(stderr, "afppasswd: can't safely replace %s: %s\n", path,
232 strerror(errno));
233 close(fd);
234 return -1;
235 }
236
237 return fd;
238}
239
240static int randnum_make_keypath(const char *path, char *keypath,
241 size_t keypath_size)
242{
243 size_t path_len = strnlen(path, keypath_size);
244
245 if (path_len > keypath_size - sizeof(".key")) {
246 fprintf(stderr,
247 "afppasswd: Randnum password file path is too long to locate companion key file.\n");
248 return -1;
249 }
250
251 strlcpy(keypath, path, keypath_size);
252 strlcat(keypath, ".key", keypath_size);
253 return 0;
254}
255
256static int randnum_read_keyfd(int keyfd, uint8_t key[DES_KEY_SZ],
257 const char *keypath)
258{
259 uint8_t encoded[HEXPASSWDLEN + 2];
260 ssize_t keylen;
261
262 if (lseek(keyfd, 0, SEEK_SET) < 0) {
263 fprintf(stderr, "afppasswd: could not seek Randnum key file%s%s: %s\n",
264 keypath ? " " : "", keypath ? keypath : "", strerror(errno));
265 explicit_bzero(encoded, sizeof(encoded));
266 return -1;
267 }
268
269 keylen = read(keyfd, encoded, sizeof(encoded));
270
271 if (keylen < 0) {
272 fprintf(stderr, "afppasswd: could not read Randnum key file%s%s: %s\n",
273 keypath ? " " : "", keypath ? keypath : "", strerror(errno));
274 explicit_bzero(encoded, sizeof(encoded));
275 return -1;
276 }
277
278 if (keylen != HEXPASSWDLEN &&
279 (keylen != HEXPASSWDLEN + 1 || encoded[HEXPASSWDLEN] != '\n')) {
280 fprintf(stderr,
281 "afppasswd: invalid Randnum key file%s%s: expected 16 hexadecimal characters with an optional trailing newline.\n",
282 keypath ? " " : "", keypath ? keypath : "");
283 explicit_bzero(encoded, sizeof(encoded));
284 return -1;
285 }
286
287 for (int i = 0; i < HEXPASSWDLEN; i++) {
288 if (!isxdigit(encoded[i])) {
289 fprintf(stderr,
290 "afppasswd: invalid Randnum key file%s%s: expected hexadecimal characters only.\n",
291 keypath ? " " : "", keypath ? keypath : "");
292 explicit_bzero(encoded, sizeof(encoded));
293 return -1;
294 }
295 }
296
297 for (int i = 0, j = 0; i < HEXPASSWDLEN; i += 2, j++) {
298 key[j] = (uint8_t)((unhex(encoded[i]) << 4) | unhex(encoded[i + 1]));
299 }
300
301 explicit_bzero(encoded, sizeof(encoded));
302 return 0;
303}
304
305static int randnum_open_keyfile(const char *path, int *keyfd_out)
306{
307 char keypath[MAXPATHLEN + 1];
308 uint8_t key[DES_KEY_SZ];
309 int keyfd;
310
311 if (randnum_make_keypath(path, keypath, sizeof(keypath)) < 0) {
312 return -1;
313 }
314
315 keyfd = open_credential_file(keypath, O_RDONLY);
316
317 if (keyfd < 0) {
318 fprintf(stderr, "afppasswd: required Randnum key file is unavailable.\n");
319 return -1;
320 }
321
322 if (randnum_read_keyfd(keyfd, key, keypath) < 0) {
323 close(keyfd);
324 explicit_bzero(key, sizeof(key));
325 return -1;
326 }
327
328 explicit_bzero(key, sizeof(key));
329 *keyfd_out = keyfd;
330 return 0;
331}
332
333static int randnum_write_keyfile(const char *keypath)
334{
335 uint8_t key[DES_KEY_SZ];
336 char encoded[HEXPASSWDLEN + 1];
337 int fd;
338 gcry_randomize(key, sizeof(key), GCRY_STRONG_RANDOM);
339
340 for (int i = 0, j = 0; i < DES_KEY_SZ; i++, j += 2) {
341 encoded[j] = hextable[(key[i] & 0xF0) >> 4];
342 encoded[j + 1] = hextable[key[i] & 0x0F];
343 }
344
345 encoded[HEXPASSWDLEN] = '\n';
347
348 if (fd < 0) {
349 explicit_bzero(key, sizeof(key));
350 explicit_bzero(encoded, sizeof(encoded));
351 return -1;
352 }
353
354 if (write(fd, encoded, sizeof(encoded)) != (ssize_t)sizeof(encoded)) {
355 fprintf(stderr, "afppasswd: problem writing Randnum key file %s: %s\n",
356 keypath, strerror(errno));
357 close(fd);
358 explicit_bzero(key, sizeof(key));
359 explicit_bzero(encoded, sizeof(encoded));
360 return -1;
361 }
362
363 close(fd);
364 explicit_bzero(key, sizeof(key));
365 explicit_bzero(encoded, sizeof(encoded));
366 return 0;
367}
368
369static int randnum_ensure_keyfile(const char *path, int flags)
370{
371 char keypath[MAXPATHLEN + 1];
372 uint8_t key[DES_KEY_SZ];
373 int keyfd;
374
375 if (randnum_make_keypath(path, keypath, sizeof(keypath)) < 0) {
376 return -1;
377 }
378
379 keyfd = open(keypath, O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
380
381 if (keyfd < 0) {
382 if (errno != ENOENT) {
383 fprintf(stderr, "afppasswd: can't open Randnum key file %s: %s\n",
384 keypath, strerror(errno));
385 return -1;
386 }
387
388 return randnum_write_keyfile(keypath);
389 }
390
391 if (validate_opened_file(keyfd, keypath) < 0) {
392 close(keyfd);
393 return -1;
394 }
395
396 if (randnum_read_keyfd(keyfd, key, keypath) == 0) {
397 close(keyfd);
398 explicit_bzero(key, sizeof(key));
399 return 0;
400 }
401
402 close(keyfd);
403 explicit_bzero(key, sizeof(key));
404
405 if (!(flags & OPT_FORCE)) {
406 fprintf(stderr,
407 "afppasswd: use -f with -r -c to replace invalid Randnum key file %s.\n",
408 keypath);
409 return -1;
410 }
411
412 return randnum_write_keyfile(keypath);
413}
414
415/* if newpwd is null, convert passwd_buf from hex to binary. if newpwd isn't
416 * null, convert newpwd to hex and save it in passwd_buf. */
417static int convert_passwd(char *passwd_buf, char *newpwd, const int keyfd)
418{
419 uint8_t key[DES_KEY_SZ];
420 unsigned int i, j;
421 gcry_cipher_hd_t ctx = NULL;
422 gcry_error_t ctxerror;
423
424 if (!newpwd) {
425 /* convert to binary */
426 for (i = j = 0; i < HEXPASSWDLEN; i += 2, j++) {
427 passwd_buf[j] = (char)(uint8_t)((unhex(passwd_buf[i]) << 4) |
428 unhex(passwd_buf[i + 1]));
429 }
430
431 if (j <= DES_KEY_SZ) {
432 memset(passwd_buf + j, 0, HEXPASSWDLEN - j);
433 }
434 }
435
436 if (keyfd < 0 || randnum_read_keyfd(keyfd, key, NULL) < 0) {
437 return -1;
438 }
439
440 ctxerror = gcry_cipher_open(&ctx, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
441
442 if (ctxerror) {
443 fprintf(stderr, "afppasswd: gcry_cipher_open failed: %s\n",
444 gcry_strerror(ctxerror));
445 explicit_bzero(key, sizeof(key));
446 return -1;
447 }
448
449 ctxerror = gcry_cipher_setkey(ctx, key, DES_KEY_SZ);
450 explicit_bzero(key, sizeof(key));
451
452 if (ctxerror) {
453 fprintf(stderr, "afppasswd: gcry_cipher_setkey failed: %s\n",
454 gcry_strerror(ctxerror));
455 gcry_cipher_close(ctx);
456 return -1;
457 }
458
459 if (newpwd) {
460 ctxerror = gcry_cipher_encrypt(ctx, newpwd, DES_KEY_SZ, NULL, 0);
461 } else {
462 /* decrypt the password */
463 ctxerror = gcry_cipher_decrypt(ctx, passwd_buf, DES_KEY_SZ, NULL, 0);
464 }
465
466 if (ctxerror) {
467 fprintf(stderr, "afppasswd: Randnum password conversion failed: %s\n",
468 gcry_strerror(ctxerror));
469 gcry_cipher_close(ctx);
470 return -1;
471 }
472
473 gcry_cipher_close(ctx);
474
475 if (newpwd) {
476 /* convert to hex */
477 for (i = j = 0; i < DES_KEY_SZ; i++, j += 2) {
478 passwd_buf[j] = hextable[(newpwd[i] & 0xF0) >> 4];
479 passwd_buf[j + 1] = hextable[newpwd[i] & 0x0F];
480 }
481 }
482
483 return 0;
484}
485
486/* -------------------- SRP verifier functions -------------------- */
487
488/*
489 * Compute SRP verifier: x = SHA1(salt | SHA1(username | ":" | password)),
490 * then v = g^x mod N.
491 */
492static int srp_compute_verifier(const char *username, const char *password,
493 const unsigned char *salt,
494 unsigned char *v_out)
495{
496 gcry_md_hd_t hd;
497 unsigned char inner_hash[SRP_SHA1_LEN];
498 unsigned char x_hash[SRP_SHA1_LEN];
499 gcry_mpi_t x = NULL, v = NULL, g = NULL, N = NULL;
500 size_t nwritten;
501 size_t username_len;
502 size_t password_len;
503 username_len = strnlen(username, SRP_USERNAME_MAX_LEN + 1);
504
505 if (username_len > SRP_USERNAME_MAX_LEN) {
506 return -1;
507 }
508
509 password_len = strnlen(password, SRP_PASSWDLEN + 1);
510
511 if (password_len > SRP_PASSWDLEN) {
512 return -1;
513 }
514
515 /* inner = SHA1(username | ":" | password) */
516 if (gcry_md_open(&hd, GCRY_MD_SHA1, 0) != 0) {
517 return -1;
518 }
519
520 gcry_md_write(hd, username, username_len);
521 gcry_md_write(hd, ":", 1);
522 gcry_md_write(hd, password, password_len);
523 memcpy(inner_hash, gcry_md_read(hd, GCRY_MD_SHA1), SRP_SHA1_LEN);
524 gcry_md_close(hd);
525
526 /* x = SHA1(salt | inner) */
527 if (gcry_md_open(&hd, GCRY_MD_SHA1, 0) != 0) {
528 return -1;
529 }
530
531 gcry_md_write(hd, salt, SRP_SALT_LEN);
532 gcry_md_write(hd, inner_hash, SRP_SHA1_LEN);
533 memcpy(x_hash, gcry_md_read(hd, GCRY_MD_SHA1), SRP_SHA1_LEN);
534 gcry_md_close(hd);
535 /* v = g^x mod N */
536 gcry_mpi_scan(&x, GCRYMPI_FMT_USG, x_hash, SRP_SHA1_LEN, NULL);
537 gcry_mpi_scan(&N, GCRYMPI_FMT_USG, srp_N_bytes, SRP_NBYTES, NULL);
538 g = gcry_mpi_set_ui(NULL, srp_g_byte);
539 v = gcry_mpi_new(0);
540 gcry_mpi_powm(v, g, x, N);
541 /* Write v as SRP_NBYTES big-endian, zero-padded */
542 memset(v_out, 0, SRP_NBYTES);
543 gcry_mpi_print(GCRYMPI_FMT_USG, v_out, SRP_NBYTES, &nwritten, v);
544
545 if (nwritten < SRP_NBYTES) {
546 memmove(v_out + SRP_NBYTES - nwritten, v_out, nwritten);
547 memset(v_out, 0, SRP_NBYTES - nwritten);
548 }
549
550 explicit_bzero(inner_hash, sizeof(inner_hash));
551 explicit_bzero(x_hash, sizeof(x_hash));
552 gcry_mpi_release(x);
553 gcry_mpi_release(v);
554 gcry_mpi_release(g);
555 gcry_mpi_release(N);
556 return 0;
557}
558
559/*
560 * Write hex-encoded salt and verifier to a buffer.
561 * out_hex must have room for SRP_HEX_SALT_LEN + 1 + SRP_HEX_V_LEN bytes.
562 */
563static void srp_encode_hex(char *out_hex, const unsigned char *salt,
564 const unsigned char *verifier)
565{
566 for (int i = 0; i < SRP_SALT_LEN; i++) {
567 out_hex[i * 2] = hextable[(salt[i] >> 4) & 0x0F];
568 out_hex[i * 2 + 1] = hextable[salt[i] & 0x0F];
569 }
570
571 out_hex[SRP_HEX_SALT_LEN] = ':';
572
573 for (int i = 0; i < SRP_NBYTES; i++) {
574 out_hex[SRP_HEX_SALT_LEN + 1 + i * 2] =
575 hextable[(verifier[i] >> 4) & 0x0F];
576 out_hex[SRP_HEX_SALT_LEN + 1 + i * 2 + 1] =
577 hextable[verifier[i] & 0x0F];
578 }
579}
580
581static int open_srp_verifier_directory(const char *path)
582{
583 struct stat st;
584 int fd = open(path, O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
585
586 if (fd < 0) {
587 if (lstat(path, &st) == 0 && S_ISREG(st.st_mode)) {
588 fprintf(stderr,
589 "afppasswd: %s is a legacy flat SRP verifier file; stop afpd and run 'afppasswd -m'.\n",
590 path);
591 } else {
592 fprintf(stderr,
593 "afppasswd: can't open SRP verifier directory %s: %s\n",
594 path, strerror(errno));
595 }
596
597 return -1;
598 }
599
600 if (fstat(fd, &st) < 0 || !S_ISDIR(st.st_mode) || st.st_uid != 0 ||
601 (st.st_mode & (S_IWGRP | S_IWOTH))) {
602 fprintf(stderr,
603 "afppasswd: SRP verifier directory %s must be root-owned and not writable by group or other.\n",
604 path);
605 close(fd);
606 return -1;
607 }
608
609 return fd;
610}
611
612static int srp_uid_filename(uid_t uid, char *name, size_t size)
613{
614 int len = snprintf(name, size, "%ju", (uintmax_t)uid);
615 return len < 0 || (size_t)len >= size ? -1 : 0;
616}
617
618static int validate_srp_verifier_file(int fd, uid_t uid, const char *path,
619 int administrative)
620{
621 struct stat st;
622
623 if (fstat(fd, &st) < 0 || !S_ISREG(st.st_mode) ||
624 (st.st_uid != uid && !(administrative && st.st_uid == 0)) ||
625 (!administrative && !srp_verifier_mode_is_safe(st.st_mode)) ||
626 st.st_nlink != 1) {
627 fprintf(stderr,
628 "afppasswd: verifier in %s must be a single-link regular file owned by uid %ju%s.\n",
629 path, (uintmax_t)uid,
630 administrative ? " or root" : " and accessible only by its owner");
631 return -1;
632 }
633
634 return 0;
635}
636
637static int open_srp_verifier(int dirfd, const char *path, uid_t uid,
638 int create)
639{
640 char uid_name[3 * sizeof(uid_t) + 1];
641 int fd;
642 int created = 0;
643
644 if (srp_uid_filename(uid, uid_name, sizeof(uid_name)) < 0) {
645 return -1;
646 }
647
648 fd = openat(dirfd, uid_name, O_RDWR | O_CLOEXEC | O_NOFOLLOW |
649 (create ? O_CREAT | O_EXCL : 0), 0600);
650
651 if (fd < 0 && create && errno == EEXIST) {
652 fd = openat(dirfd, uid_name, O_RDWR | O_CLOEXEC | O_NOFOLLOW);
653 } else if (fd >= 0) {
654 created = create;
655 }
656
657 if (fd < 0) {
658 if (!create && errno == EACCES) {
659 /* A root-owned placeholder denotes a user who is not enrolled. */
660 fprintf(stderr,
661 "Your password is disabled. Please see your administrator.\n");
662 } else {
663 fprintf(stderr, "afppasswd: can't open verifier %s/%s: %s\n",
664 path, uid_name, strerror(errno));
665 }
666
667 return -1;
668 }
669
670 /* Administrative callers may repair permissions, but must first validate
671 * the file type, ownership, and link count without changing metadata. */
672 if (validate_srp_verifier_file(fd, created ? 0 : uid, path,
673 create) < 0) {
674 close(fd);
675 return -1;
676 }
677
678 /* Root's -a and -d, as well as initialization, normalize permissions before
679 * reading or writing credentials. New files remain root-owned until -a. */
680 if (create && fchmod(fd, 0600) < 0) {
681 fprintf(stderr, "afppasswd: can't prepare verifier %s/%s: %s\n",
682 path, uid_name, strerror(errno));
683 close(fd);
684 return -1;
685 }
686
687 return fd;
688}
689
690static int update_srp_passwd(const char *path, const char *name, uid_t uid,
691 int flags, const char *pass)
692{
693 char *passwd = NULL;
694 char password[SRP_PASSWDLEN + 1] = {0};
695 FILE *fp = NULL;
696 int err = 0;
697 int dirfd = -1, fd;
698 const char *p = NULL;
699 size_t pass_len;
700 size_t name_len;
701 unsigned char old_salt[SRP_SALT_LEN] = {0};
702 unsigned char old_v[SRP_NBYTES] = {0};
703 unsigned char check_v[SRP_NBYTES] = {0};
704 unsigned char new_salt[SRP_SALT_LEN] = {0};
705 unsigned char new_v[SRP_NBYTES] = {0};
706 char hex_buf[SRP_HEX_SALT_LEN + 1 + SRP_HEX_V_LEN] = {0};
707 /* line buffer: username + ":" + hex_salt + ":" + hex_verifier + "\n" + NUL */
708 char line[SRP_USERNAME_MAX_LEN + SRP_FORMAT_LEN + 1] = {0};
709
710 if ((flags & OPT_ADDUSER) && !(flags & OPT_ISROOT)) {
711 fprintf(stderr, "afppasswd: only root can add a user.\n");
712 return -1;
713 }
714
715 if (!srp_valid_username(name)) {
716 fprintf(stderr, "afppasswd: invalid username.\n");
717 return -1;
718 }
719
720 pass_len = strnlen(pass, SRP_PASSWDLEN + 1);
721
722 if (pass_len > SRP_PASSWDLEN) {
723 fprintf(stderr, "afppasswd: max SRP password length is %d.\n", SRP_PASSWDLEN);
724 return -1;
725 }
726
727 name_len = strnlen(name, sizeof(line));
729
730 if (dirfd < 0) {
731 return -1;
732 }
733
734 fd = open_srp_verifier(dirfd, path, uid,
735 (flags & OPT_ISROOT) && (flags & OPT_ADDUSER));
736
737 if (fd < 0) {
738 close(dirfd);
739 return -1;
740 }
741
742 if ((fp = fdopen(fd, "r+")) == NULL) {
743 fprintf(stderr, "afppasswd: can't open stream for %s: %s\n", path,
744 strerror(errno));
745 close(fd);
746 close(dirfd);
747 return -1;
748 }
749
750 /* A per-user verifier file contains at most one existing record. */
751 if (fgets(line, sizeof(line), fp) != NULL && fgetc(fp) != EOF) {
752 fprintf(stderr,
753 "afppasswd: verifier file must contain exactly one record.\n");
754 err = -1;
755 goto done;
756 }
757
758 if (ferror(fp)) {
759 fprintf(stderr, "afppasswd: can't read verifier in %s: %s\n", path,
760 strerror(errno));
761 err = -1;
762 goto done;
763 }
764
765 p = strchr(line, ':');
766
767 /* Root's add mode also permits empty files and stale usernames after uid reuse. */
768 if (p && name_len == (size_t)(p - line) && strncmp(line, name, name_len) == 0) {
769 p++;
770
771 if (!srp_valid_fields(p)) {
772 fprintf(stderr, "afppasswd: corrupt verifier file.\n");
773 err = -1;
774 goto done;
775 }
776
777 if (!(flags & OPT_ISROOT) && (*p == SRP_DISABLED_CHAR)) {
778 fprintf(stderr, "Your password is disabled. Please see your administrator.\n");
779 err = -1;
780 goto done;
781 }
782 } else if (!(flags & OPT_ADDUSER)) {
783 fprintf(stderr, "afppasswd: can't find verifier for %s in %s\n", name,
784 path);
785 err = -1;
786 goto done;
787 }
788
789 /* Verify old password for non-root users */
790 if ((flags & OPT_ISROOT) == 0) {
791 /* Recompute the verifier from the supplied old password. */
792 passwd = getpass("Enter OLD AFP password: ");
793
794 if (passwd == NULL || passwd[0] == '\0') {
795 fprintf(stderr, "afppasswd: password input canceled.\n");
796 err = -1;
797 goto done;
798 }
799
800 if (*p == SRP_DISABLED_CHAR) {
801 fprintf(stderr, "afppasswd: no existing password set.\n");
802 err = -1;
803 goto done;
804 }
805
806 for (int i = 0; i < SRP_SALT_LEN; i++) {
807 if (!isxdigit(p[i * 2]) || !isxdigit(p[i * 2 + 1])) {
808 fprintf(stderr, "afppasswd: corrupt verifier file.\n");
809 err = -1;
810 goto done;
811 }
812
813 old_salt[i] = (unsigned char)((unhex(p[i * 2]) << 4) | unhex(p[i * 2 + 1]));
814 }
815
816 /* Parse existing verifier */
817 const char *vp = p + SRP_HEX_SALT_LEN + 1; /* skip salt + colon */
818
819 for (int i = 0; i < SRP_NBYTES; i++) {
820 if (!isxdigit(vp[i * 2]) || !isxdigit(vp[i * 2 + 1])) {
821 fprintf(stderr, "afppasswd: corrupt verifier file.\n");
822 err = -1;
823 goto done;
824 }
825
826 old_v[i] = (unsigned char)((unhex(vp[i * 2]) << 4) | unhex(vp[i * 2 + 1]));
827 }
828
829 /* Recompute verifier from entered password and compare */
830 if (srp_compute_verifier(name, passwd, old_salt, check_v) != 0) {
831 fprintf(stderr, "afppasswd: internal error computing verifier.\n");
832 err = -1;
833 goto done;
834 }
835
836 if (atalk_ct_memcmp(check_v, old_v, SRP_NBYTES) != 0) {
837 fprintf(stderr, "afppasswd: invalid password.\n");
838 explicit_bzero(check_v, sizeof(check_v));
839 err = -1;
840 goto done;
841 }
842
843 explicit_bzero(check_v, sizeof(check_v));
844 }
845
846 /* Get new password */
847 if (pass_len < 1) {
848 passwd = getpass("Enter NEW AFP password: ");
849
850 if (passwd == NULL || passwd[0] == '\0') {
851 fprintf(stderr, "afppasswd: password input canceled.\n");
852 err = -1;
853 goto done;
854 }
855
856 size_t passwd_len = strnlen(passwd, SRP_PASSWDLEN + 1);
857
858 if (passwd_len > SRP_PASSWDLEN) {
859 fprintf(stderr, "afppasswd: max SRP password length is %d.\n", SRP_PASSWDLEN);
860 err = -1;
861 goto done;
862 }
863
864 memcpy(password, passwd, passwd_len + 1);
865 } else {
866 strlcpy(password, pass, sizeof(password));
867 }
868
869#ifdef USE_CRACKLIB
870
871 if (!(flags & OPT_NOCRACK)) {
872 const char *pwcheck = FascistCheck(password, _PATH_CRACKLIB);
873
874 if (pwcheck) {
875 fprintf(stderr, "Error: %s\n", pwcheck);
876 err = -1;
877 goto done;
878 }
879 }
880
881#endif
882
883 if (pass_len < 1) {
884 passwd = getpass("Enter NEW AFP password again: ");
885
886 if (passwd == NULL || passwd[0] == '\0' ||
887 strcmp(passwd, password) != 0) {
888 fprintf(stderr, "afppasswd: passwords don't match!\n");
889 err = -1;
890 goto done;
891 }
892 }
893
894 /* Generate new salt and compute verifier */
895 gcry_randomize(new_salt, SRP_SALT_LEN, GCRY_STRONG_RANDOM);
896
897 if (srp_compute_verifier(name, password, new_salt, new_v) != 0) {
898 fprintf(stderr, "afppasswd: failed to compute verifier.\n");
899 err = -1;
900 goto done;
901 }
902
903 /* Encode as hex */
904 srp_encode_hex(hex_buf, new_salt, new_v);
905 /* Replace the single record from the start of the file. */
906 {
907 struct flock lock = {0};
908 int expected_len = (int)(name_len + 1 + sizeof(hex_buf) + 1);
909 int written;
910 lock.l_type = F_WRLCK;
911 lock.l_start = 0;
912 lock.l_len = 0;
913 lock.l_whence = SEEK_SET;
914
915 if (fcntl(fd, F_SETLK, &lock) < 0 || fseek(fp, 0, SEEK_SET) != 0) {
916 fprintf(stderr, "afppasswd: can't lock or seek %s: %s\n", path,
917 strerror(errno));
918 err = -1;
919 goto done;
920 }
921
922 /* Write: username:hex_salt:hex_verifier\n */
923 written = fprintf(fp, "%s:%.*s\n", name, (int)sizeof(hex_buf), hex_buf);
924
925 if (written != expected_len || fflush(fp) != 0 ||
926 ftruncate(fd, expected_len) < 0 || fsync(fd) < 0) {
927 fprintf(stderr, "afppasswd: problem writing to %s: %s\n", path,
928 strerror(errno));
929 err = -1;
930 }
931
932 /* Ownership grants SRP enrollment, only after the verifier is durable. */
933 if (err == 0 && (flags & OPT_ISROOT) &&
934 (fchown(fd, uid, (gid_t) -1) < 0 || fsync(fd) < 0)) {
935 fprintf(stderr, "afppasswd: can't enable verifier in %s: %s\n",
936 path, strerror(errno));
937 err = -1;
938 }
939
940 lock.l_type = F_UNLCK;
941
942 if (fcntl(fd, F_SETLK, &lock) < 0) {
943 fprintf(stderr, "afppasswd: can't unlock %s: %s\n", path,
944 strerror(errno));
945 err = -1;
946 }
947 }
948
949 if (err == 0) {
950 printf("afppasswd: updated SRP verifier.\n");
951 }
952
953done:
954
955 if (passwd != NULL) {
956 explicit_bzero(passwd, strnlen(passwd, SRP_PASSWDLEN + 1));
957 }
958
959 explicit_bzero(new_salt, sizeof(new_salt));
960 explicit_bzero(new_v, sizeof(new_v));
961 explicit_bzero(old_salt, sizeof(old_salt));
962 explicit_bzero(old_v, sizeof(old_v));
963 explicit_bzero(check_v, sizeof(check_v));
964 explicit_bzero(hex_buf, sizeof(hex_buf));
965 explicit_bzero(password, sizeof(password));
966 explicit_bzero(line, sizeof(line));
967 fclose(fp);
968 close(dirfd);
969 return err;
970}
971
972/* Disable an SRP account by installing a root-owned placeholder. */
973static int disable_srp_verifier(const char *path, const char *name, uid_t uid)
974{
975 char line[SRP_USERNAME_MAX_LEN + SRP_FORMAT_LEN + 1];
976 struct flock lock = {0};
977 int dirfd = -1, fd = -1;
978 size_t name_len;
979 int length;
980 int err = -1;
981
982 if (!srp_valid_username(name)) {
983 fprintf(stderr, "afppasswd: invalid username.\n");
984 return -1;
985 }
986
987 name_len = strnlen(name, SRP_USERNAME_MAX_LEN + 1);
988 length = snprintf(line, sizeof(line), "%s:", name);
989
990 if (length < 0 || (size_t)length != name_len + 1 ||
991 (size_t)length + SRP_FIELDS_LEN >
992 sizeof(line)) {
993 fprintf(stderr, "afppasswd: username is too long.\n");
994 return -1;
995 }
996
997 memset(line + length, SRP_DISABLED_CHAR, SRP_HEX_SALT_LEN);
998 length += SRP_HEX_SALT_LEN;
999 line[length++] = ':';
1000 memset(line + length, SRP_DISABLED_CHAR, SRP_HEX_V_LEN);
1001 length += SRP_HEX_V_LEN;
1002 line[length++] = '\n';
1003
1004 if ((dirfd = open_srp_verifier_directory(path)) < 0) {
1005 return -1;
1006 }
1007
1008 /* Creation makes disable idempotent and handles uid-reuse cleanup. */
1009 if ((fd = open_srp_verifier(dirfd, path, uid, 1)) < 0) {
1010 goto done;
1011 }
1012
1013 lock.l_type = F_WRLCK;
1014 lock.l_start = 0;
1015 lock.l_len = 0;
1016 lock.l_whence = SEEK_SET;
1017
1018 /* Do not let a user-held advisory lock block an administrator forever. */
1019 if (fcntl(fd, F_SETLK, &lock) < 0) {
1020 fprintf(stderr, "afppasswd: can't lock verifier in %s: %s\n", path,
1021 strerror(errno));
1022 goto done;
1023 }
1024
1025 /* Revoke enrolment before replacing the record. */
1026 if (fchown(fd, 0, (gid_t) -1) < 0 || fchmod(fd, 0600) < 0 ||
1027 ftruncate(fd, 0) < 0 || lseek(fd, 0, SEEK_SET) < 0 ||
1028 write(fd, line, length) != length || fsync(fd) < 0 ||
1029 fsync(dirfd) < 0) {
1030 fprintf(stderr, "afppasswd: can't disable verifier in %s: %s\n", path,
1031 strerror(errno));
1032 goto unlock;
1033 }
1034
1035 err = 0;
1036unlock:
1037 lock.l_type = F_UNLCK;
1038
1039 if (fcntl(fd, F_SETLK, &lock) < 0) {
1040 fprintf(stderr, "afppasswd: can't unlock verifier in %s: %s\n", path,
1041 strerror(errno));
1042 err = -1;
1043 }
1044
1045done:
1046
1047 if (fd >= 0) {
1048 close(fd);
1049 }
1050
1051 close(dirfd);
1052 explicit_bzero(line, sizeof(line));
1053 return err;
1054}
1055
1056static int create_srp_directory(const char *path, uid_t minuid)
1057{
1058 struct passwd *pwd;
1059 int dirfd, err = 0;
1060
1061 if (mkdir(path, 0755) < 0) {
1062 fprintf(stderr, "afppasswd: can't create SRP verifier directory %s: %s\n",
1063 path, strerror(errno));
1064 return -1;
1065 }
1066
1067 if ((dirfd = open_srp_verifier_directory(path)) < 0) {
1068 return -1;
1069 }
1070
1071 if (fchmod(dirfd, 0755) < 0) {
1072 fprintf(stderr,
1073 "afppasswd: can't set permissions on SRP verifier directory %s: %s\n",
1074 path, strerror(errno));
1075 close(dirfd);
1076 return -1;
1077 }
1078
1079 setpwent();
1080
1081 while ((pwd = getpwent())) {
1082 if (pwd->pw_uid < minuid) {
1083 continue;
1084 }
1085
1086 int fd;
1087 /* username + ":" + placeholder salt + ":" + placeholder verifier + "\n" */
1088 size_t namelen = strnlen(pwd->pw_name, sizeof(buf));
1089
1090 if (!srp_valid_username(pwd->pw_name) || namelen == sizeof(buf) ||
1091 namelen + SRP_FORMAT_LEN > sizeof(buf) - 1) {
1092 continue;
1093 }
1094
1095 int n = snprintf(buf, sizeof(buf), "%s:", pwd->pw_name);
1096
1097 /* Placeholder asterisks for salt */
1098 for (int i = 0; i < SRP_HEX_SALT_LEN; i++) {
1099 buf[n++] = SRP_DISABLED_CHAR;
1100 }
1101
1102 buf[n++] = ':';
1103
1104 /* Placeholder asterisks for verifier */
1105 for (int i = 0; i < SRP_HEX_V_LEN; i++) {
1106 buf[n++] = SRP_DISABLED_CHAR;
1107 }
1108
1109 buf[n++] = '\n';
1110 fd = open_srp_verifier(dirfd, path, pwd->pw_uid, 1);
1111
1112 if (fd < 0) {
1113 err = -1;
1114 break;
1115 }
1116
1117 /* Reinitialization must revoke enrollment for existing files too. */
1118 if (fchown(fd, 0, (gid_t) -1) < 0 || ftruncate(fd, 0) < 0 ||
1119 lseek(fd, 0, SEEK_SET) < 0 || write(fd, buf, n) != n ||
1120 fsync(fd) < 0) {
1121 fprintf(stderr, "afppasswd: problem writing to %s: %s\n",
1122 path, strerror(errno));
1123 err = -1;
1124 close(fd);
1125 break;
1126 }
1127
1128 close(fd);
1129 }
1130
1131 endpwent();
1132 close(dirfd);
1133 return err;
1134}
1135
1136/* -------------------- RandNum (legacy) functions -------------------- */
1137
1138static int valid_hex_or_disabled(const char *field, size_t len)
1139{
1140 int disabled = 1;
1141
1142 for (size_t i = 0; i < len; i++) {
1143 disabled &= field[i] == PASSWD_ILLEGAL;
1144 }
1145
1146 if (disabled) {
1147 return 1;
1148 }
1149
1150 for (size_t i = 0; i < len; i++) {
1151 if (!isxdigit((unsigned char)field[i])) {
1152 return 0;
1153 }
1154 }
1155
1156 return 1;
1157}
1158
1159static int valid_randnum_record(const char *fields)
1160{
1161 size_t fields_len = FORMAT_LEN - 1;
1162 return strlen(fields) == fields_len && fields[HEXPASSWDLEN] == ':' &&
1163 fields[HEXPASSWDLEN * 2 + 1] == ':' &&
1164 fields[fields_len - 1] == '\n' &&
1167 valid_hex_or_disabled(fields + HEXPASSWDLEN * 2 + 2, 8);
1168}
1169
1170/* this matches the code in uam_randnum.c */
1171static int update_passwd(const char *path, const char *name, int flags,
1172 const char *pass)
1173{
1174 char password[PASSWDLEN + 1] = {0}, *p = NULL, *passwd = "";
1175 FILE *fp = NULL;
1176 off_t pos = 0;
1177 int fd, keyfd = -1, err = 0, new_entry = 0;
1178 size_t pass_len;
1179 size_t name_len;
1180
1181 if (!(flags & OPT_ISROOT)) {
1182 fprintf(stderr, "afppasswd: only root can manage RandNum passwords.\n");
1183 return -1;
1184 }
1185
1186 if (!srp_valid_username(name)) {
1187 fprintf(stderr, "afppasswd: invalid username.\n");
1188 return -1;
1189 }
1190
1191 if (randnum_open_keyfile(path, &keyfd) < 0) {
1192 return -1;
1193 }
1194
1195 fd = open_credential_file(path, O_RDWR);
1196
1197 if (fd < 0) {
1198 close(keyfd);
1199 return -1;
1200 }
1201
1202 if ((fp = fdopen(fd, "r+")) == NULL) {
1203 fprintf(stderr, "afppasswd: can't open stream for %s: %s\n", path,
1204 strerror(errno));
1205 close(fd);
1206 close(keyfd);
1207 return -1;
1208 }
1209
1210 pass_len = strnlen(pass, PASSWDLEN + 1);
1211 name_len = strnlen(name, sizeof(buf));
1212 pos = ftell(fp);
1213 memset(buf, 0, sizeof(buf));
1214
1215 while (fgets(buf, sizeof(buf), fp)) {
1216 p = strchr(buf, ':');
1217
1218 /* check for a match */
1219 if (p && name_len == (size_t)(p - buf) && strncmp(buf, name, name_len) == 0) {
1220 p++;
1221
1222 if (!valid_randnum_record(p)) {
1223 fprintf(stderr, "afppasswd: corrupt Randnum password file.\n");
1224 err = -1;
1225 goto update_done;
1226 }
1227
1228 if (!(flags & OPT_ISROOT) && (*p == PASSWD_ILLEGAL)) {
1229 fprintf(stderr, "Your password is disabled. Please see your administrator.\n");
1230 err = -1;
1231 goto update_done;
1232 }
1233
1234 goto found_entry;
1235 }
1236
1237 pos = ftell(fp);
1238 memset(buf, 0, sizeof(buf));
1239 }
1240
1241 if (flags & OPT_ADDUSER) {
1242 /* Build the complete new record without changing the file. */
1243 strlcpy(buf, name, sizeof(buf));
1244 strlcat(buf, FORMAT, sizeof(buf));
1245 p = strchr(buf, ':') + 1;
1246
1247 if (find_append_position(fp, path, &pos) < 0) {
1248 err = -1;
1249 goto update_done;
1250 }
1251
1252 new_entry = 1;
1253 } else {
1254 fprintf(stderr, "afppasswd: can't find %s in %s\n", name, path);
1255 err = -1;
1256 goto update_done;
1257 }
1258
1259found_entry:
1260
1261 /* need to verify against old password */
1262 if ((flags & OPT_ISROOT) == 0) {
1263 passwd = getpass("Enter OLD AFP password: ");
1264
1265 if (passwd == NULL || passwd[0] == '\0') {
1266 fprintf(stderr, "afppasswd: password input canceled.\n");
1267 err = -1;
1268 goto update_done;
1269 }
1270
1271 if (convert_passwd(p, NULL, keyfd) < 0) {
1272 err = -1;
1273 goto update_done;
1274 }
1275
1276 if (strncmp(passwd, p, PASSWDLEN)) {
1277 fprintf(stderr, "afppasswd: invalid password.\n");
1278 err = -1;
1279 goto update_done;
1280 }
1281 }
1282
1283 /* new password */
1284 if (pass_len < 1) {
1285 passwd = getpass("Enter NEW AFP password: ");
1286
1287 if (passwd == NULL || passwd[0] == '\0') {
1288 fprintf(stderr, "afppasswd: password input canceled.\n");
1289 err = -1;
1290 goto update_done;
1291 }
1292
1293 size_t passwd_len = strnlen(passwd, PASSWDLEN + 1);
1294
1295 if (passwd_len > PASSWDLEN) {
1296 fprintf(stderr, "afppasswd: max RandNum password length is %d.\n", PASSWDLEN);
1297 err = -1;
1298 goto update_done;
1299 }
1300
1301 /* Make sure we null out any remaining bytes of the input string */
1302 if (passwd_len < PASSWDLEN) {
1303 for (int s = (int) passwd_len; s <= PASSWDLEN; s++) {
1304 passwd[s] = '\0';
1305 }
1306 }
1307
1308 memcpy(password, passwd, sizeof(password));
1309 } else {
1310 memcpy(password, pass, sizeof(password));
1311
1312 if (pass_len < PASSWDLEN) {
1313 for (int i = (int) pass_len; i <= PASSWDLEN; i++) {
1314 password[i] = '\0';
1315 }
1316 }
1317 }
1318
1319 password[PASSWDLEN] = '\0';
1320#ifdef USE_CRACKLIB
1321
1322 if (!(flags & OPT_NOCRACK)) {
1323 const char *pwcheck = FascistCheck(password, _PATH_CRACKLIB);
1324
1325 if (pwcheck) {
1326 fprintf(stderr, "Error: %s\n", pwcheck);
1327 err = -1;
1328 goto update_done;
1329 }
1330 }
1331
1332#endif /* USE_CRACKLIB */
1333
1334 if (pass_len < 1) {
1335 passwd = getpass("Enter NEW AFP password again: ");
1336 }
1337
1338 if ((passwd != NULL && passwd[0] != '\0' &&
1339 strcmp(passwd, password) == 0) || pass_len > 0) {
1340 struct flock lock = {0};
1341 size_t write_len;
1342
1343 if (convert_passwd(p, password, keyfd) < 0) {
1344 err = -1;
1345 goto update_done;
1346 }
1347
1348 lock.l_type = F_WRLCK;
1349 lock.l_start = 0;
1350 lock.l_len = 0;
1351 lock.l_whence = SEEK_SET;
1352
1353 if (fcntl(fd, F_SETLKW, &lock) < 0 || fseek(fp, pos, SEEK_SET) < 0) {
1354 fprintf(stderr, "afppasswd: can't lock or seek %s: %s\n", path,
1355 strerror(errno));
1356 err = -1;
1357 goto update_done;
1358 }
1359
1360 write_len = new_entry ? strnlen(buf, sizeof(buf)) :
1361 (size_t)(p - buf) + HEXPASSWDLEN;
1362
1363 if (fwrite(buf, 1, write_len, fp) != write_len || fflush(fp) != 0) {
1364 fprintf(stderr, "afppasswd: problem writing to %s: %s\n", path,
1365 strerror(errno));
1366 err = -1;
1367 }
1368
1369 lock.l_type = F_UNLCK;
1370
1371 if (fcntl(fd, F_SETLK, &lock) < 0) {
1372 fprintf(stderr, "afppasswd: can't unlock %s: %s\n", path,
1373 strerror(errno));
1374 err = -1;
1375 }
1376
1377 if (err == 0) {
1378 printf("afppasswd: updated Randnum password.\n");
1379 }
1380 } else {
1381 fprintf(stderr, "afppasswd: passwords don't match!\n");
1382 err = -1;
1383 }
1384
1385update_done:
1386
1387 if (passwd != NULL) {
1388 explicit_bzero(passwd, strnlen(passwd, PASSWDLEN + 1));
1389 }
1390
1391 explicit_bzero(password, sizeof(password));
1392 explicit_bzero(buf, sizeof(buf));
1393
1394 if (keyfd > -1) {
1395 close(keyfd);
1396 }
1397
1398 fclose(fp);
1399 return err;
1400}
1401
1402
1403/* creates a file with all the password entries */
1404static int create_file(const char *path, uid_t minuid)
1405{
1406 struct passwd *pwd;
1407 int fd, len, err = 0;
1408
1409 if ((fd = open_credential_for_replacement(path)) < 0) {
1410 return -1;
1411 }
1412
1413 setpwent();
1414
1415 while ((pwd = getpwent())) {
1416 if (pwd->pw_uid < minuid) {
1417 continue;
1418 }
1419
1420 /* a little paranoia */
1421 size_t name_len = strnlen(pwd->pw_name, sizeof(buf));
1422
1423 if (!srp_valid_username(pwd->pw_name) || name_len == sizeof(buf) ||
1424 name_len + FORMAT_LEN > sizeof(buf) - 1) {
1425 continue;
1426 }
1427
1428 strlcpy(buf, pwd->pw_name, sizeof(buf));
1429 strlcat(buf, FORMAT, sizeof(buf));
1430 len = (int)strnlen(buf, sizeof(buf));
1431
1432 if (write(fd, buf, len) != len) {
1433 fprintf(stderr, "afppasswd: problem writing to %s: %s\n", path,
1434 strerror(errno));
1435 err = -1;
1436 break;
1437 }
1438 }
1439
1440 endpwent();
1441 close(fd);
1442 return err;
1443}
1444
1445
1446static void print_usage(void)
1447{
1448 fprintf(stderr, "afppasswd (Netatalk %s)\n", VERSION);
1449#ifdef USE_CRACKLIB
1450 fprintf(stderr,
1451 "Usage (root): afppasswd [-cfmrn] [-a username | -d username] [-p directory] [-u minuid] [-w string]\n");
1452#else
1453 fprintf(stderr,
1454 "Usage (root): afppasswd [-cfmr] [-a username | -d username] [-p directory] [-u minuid] [-w string]\n");
1455#endif
1456 fprintf(stderr,
1457 "Usage (user): afppasswd\n");
1458 fprintf(stderr, " -a user add or reset password for the named user\n");
1459 fprintf(stderr,
1460 " -d user disable the named user's SRP verifier\n");
1461 fprintf(stderr,
1462 " -c create and initialize the credential store\n");
1463 fprintf(stderr,
1464 " -f replace an existing Randnum credential file with -r -c\n");
1465 fprintf(stderr, " -m migrate a legacy flat SRP verifier file\n");
1466 fprintf(stderr, " -r use legacy RandNum mode (default is SRP)\n");
1467#ifdef USE_CRACKLIB
1468 fprintf(stderr, " -n disable password strength check\n");
1469#endif
1470 fprintf(stderr, " -u uid minimum uid to use, defaults to 100\n");
1471 fprintf(stderr,
1472 " -p path path to SRP verifier directory (or Randnum password file with -r)\n");
1473 fprintf(stderr, " -w string use string as password\n");
1474}
1475
1476int main(int argc, char **argv)
1477{
1478 struct stat st;
1479 int flags;
1480 uid_t uid_min = UID_START, uid;
1481 const char *path = NULL;
1482 int adduser_seen = 0, disable_seen = 0, path_seen = 0;
1483 int password_seen = 0, uid_seen = 0;
1484 const char *pass = "";
1485 const char *add_username = NULL;
1486 const char *disable_username = NULL;
1487 int i, err = 0;
1488 extern char *optarg;
1489 extern int optind;
1490 uid = getuid();
1491
1492 /* Permanently discard an inherited setuid-root installation during an
1493 * upgrade before parsing even a path option. New installations are 0755. */
1494 if (uid != 0 && geteuid() != uid &&
1495 (setuid(uid) < 0 || geteuid() != uid)) {
1496 fprintf(stderr, "afppasswd: can't drop obsolete elevated privileges: %s\n",
1497 strerror(errno));
1498 return -1;
1499 }
1500
1501 if (initialize_libgcrypt() != 0) {
1502 return -1;
1503 }
1504
1505 flags = (uid == 0) ? OPT_ISROOT : 0;
1506
1507 while ((i = getopt(argc, argv, AFPPASSWD_OPTSTRING)) != EOF) {
1508 switch (i) {
1509 case 'c': /* create and initialize the credential store */
1510 flags |= OPT_CREATE;
1511 break;
1512
1513 case 'a': /* add a new user */
1514 flags |= OPT_ADDUSER;
1515 add_username = optarg;
1516 adduser_seen = 1;
1517 break;
1518
1519 case 'd': /* disable an SRP verifier */
1520 flags |= OPT_DISABLE;
1521 disable_username = optarg;
1522 disable_seen = 1;
1523 break;
1524
1525 case 'f': /* force an action */
1526 flags |= OPT_FORCE;
1527 break;
1528
1529 case 'm': /* migrate the legacy flat SRP verifier file */
1530 flags |= OPT_MIGRATE;
1531 break;
1532
1533 case 'r': /* legacy RandNum mode */
1534 flags |= OPT_RANDNUM;
1535 break;
1536
1537 case 'u': /* minimum uid to use. default is 100 */
1538 if (parse_minimum_uid(optarg, &uid_min) < 0) {
1539 fprintf(stderr, "afppasswd: invalid minimum uid: %s\n", optarg);
1540 err++;
1541 } else {
1542 uid_seen = 1;
1543 }
1544
1545 break;
1546#ifdef USE_CRACKLIB
1547
1548 case 'n': /* disable CRACKLIB check */
1549 flags |= OPT_NOCRACK;
1550 break;
1551#endif /* USE_CRACKLIB */
1552
1553 case 'p': /* path to SRP verifier directory or Randnum password file */
1554 path = optarg;
1555 path_seen = 1;
1556 break;
1557
1558 case 'w': /* password string */
1559 pass = optarg;
1560 password_seen = 1;
1561 break;
1562
1563 default:
1564 err++;
1565 break;
1566 }
1567 }
1568
1569 /* No positional arguments are accepted: the username, when needed,
1570 * comes from -a (root adding/updating a user), -d (root disabling a user),
1571 * or getuid() (a regular user changing their own password). */
1572 if (err || optind != argc) {
1573 print_usage();
1574 return -1;
1575 }
1576
1577 /* A regular user may only update that user's own SRP verifier. The path
1578 * names no privilege boundary now that inherited setuid privileges are
1579 * discarded above; the filesystem validates access to it. */
1580 if (!(flags & OPT_ISROOT) &&
1581 ((flags & (OPT_CREATE | OPT_FORCE | OPT_ADDUSER | OPT_NOCRACK |
1583 (flags & OPT_RANDNUM) || adduser_seen || disable_seen ||
1584 uid_seen || password_seen)) {
1585 fprintf(stderr,
1586 "ERROR: non-root users may update only their own SRP verifier.\n\n");
1587 print_usage();
1588 return -1;
1589 }
1590
1591 if ((flags & OPT_MIGRATE) &&
1592 ((flags & ~(OPT_ISROOT | OPT_MIGRATE)) ||
1593 uid_seen || password_seen)) {
1594 fprintf(stderr,
1595 "afppasswd: -m accepts only -p; stop afpd before migration.\n");
1596 print_usage();
1597 return -1;
1598 }
1599
1600 if ((flags & OPT_ADDUSER) && (flags & OPT_DISABLE)) {
1601 fprintf(stderr, "afppasswd: -a and -d cannot be combined.\n");
1602 print_usage();
1603 return -1;
1604 }
1605
1606 if ((flags & OPT_CREATE) &&
1607 ((flags & (OPT_ADDUSER | OPT_DISABLE)) || password_seen)) {
1608 fprintf(stderr, "afppasswd: -c cannot be combined with -a, -d, or -w.\n");
1609 print_usage();
1610 return -1;
1611 }
1612
1613 if (uid_seen && !(flags & OPT_CREATE)) {
1614 fprintf(stderr, "afppasswd: -u is valid only with -c.\n");
1615 print_usage();
1616 return -1;
1617 }
1618
1619 if ((flags & OPT_FORCE) &&
1620 (!(flags & OPT_RANDNUM) || !(flags & OPT_CREATE))) {
1621 fprintf(stderr, "afppasswd: -f is valid only with -r -c.\n");
1622 print_usage();
1623 return -1;
1624 }
1625
1626 if (flags & OPT_DISABLE) {
1627 if ((flags & (OPT_CREATE | OPT_FORCE | OPT_MIGRATE | OPT_RANDNUM |
1628 OPT_NOCRACK)) || uid_seen || password_seen) {
1629 fprintf(stderr, "afppasswd: -d accepts only -p.\n");
1630 print_usage();
1631 return -1;
1632 }
1633 }
1634
1635 /* Root running an update must specify the user via -a or -d. */
1636 if ((flags & OPT_ISROOT) && !(flags & OPT_CREATE) &&
1637 !(flags & OPT_ADDUSER) && !(flags & OPT_DISABLE) &&
1638 !(flags & OPT_MIGRATE)) {
1639 fprintf(stderr,
1640 "ERROR: root must specify a user with -a or -d username.\n");
1641 print_usage();
1642 return -1;
1643 }
1644
1645 if (!path_seen) {
1646 path = (flags & OPT_RANDNUM) ? _PATH_AFPDPWFILE :
1647 _PATH_AFPSRPVERIFIERPATH;
1648 }
1649
1650 /* Validate password length for RandNum mode */
1651 if ((flags & OPT_RANDNUM) && strnlen(pass, PASSWDLEN + 1) > PASSWDLEN) {
1652 fprintf(stderr, "afppasswd: max RandNum password length is %d.\n", PASSWDLEN);
1653 return -1;
1654 }
1655
1656 if (flags & OPT_MIGRATE) {
1657 return afppasswd_migrate_srp(path, 0);
1658 }
1659
1660 if (flags & OPT_CREATE) {
1661 if ((flags & OPT_ISROOT) == 0) {
1662 fprintf(stderr, "afppasswd: only root can initialize credentials.\n");
1663 return -1;
1664 }
1665
1666 i = lstat(path, &st);
1667
1668 if (!i && (!(flags & OPT_RANDNUM) || !(flags & OPT_FORCE))) {
1669 if (!(flags & OPT_RANDNUM) && S_ISREG(st.st_mode)) {
1670 fprintf(stderr,
1671 "afppasswd: %s is a legacy flat SRP verifier file; stop afpd and run 'afppasswd -m -p %s'.\n",
1672 path, path);
1673 } else {
1674 fprintf(stderr,
1675 "afppasswd: credential path already exists.\n");
1676 }
1677
1678 return -1;
1679 }
1680
1681 if (flags & OPT_RANDNUM) {
1682 if (randnum_ensure_keyfile(path, flags) < 0) {
1683 return -1;
1684 }
1685
1686 return create_file(path, uid_min);
1687 } else {
1688 return create_srp_directory(path, uid_min);
1689 }
1690 } else {
1691 struct passwd *pwd = NULL;
1692 /* Root specifies the user with -a; non-root users operate on themselves. */
1693 pwd = (flags & OPT_ISROOT) ?
1694 getpwnam((flags & OPT_DISABLE) ? disable_username : add_username) :
1695 getpwuid(uid);
1696
1697 if (pwd) {
1698 if (flags & OPT_RANDNUM) {
1699 return update_passwd(path, pwd->pw_name, flags, pass);
1700 } else if (flags & OPT_DISABLE) {
1701 return disable_srp_verifier(path, pwd->pw_name, pwd->pw_uid);
1702 } else {
1703 return update_srp_passwd(path, pwd->pw_name, pwd->pw_uid,
1704 flags, pass);
1705 }
1706 }
1707
1708 fprintf(stderr, "afppasswd: can't get password entry.\n");
1709 return -1;
1710 }
1711}
struct context * ctx
Definition afp_avahi.c:31
static void print_usage(void)
Definition afppasswd.c:1446
static int open_srp_verifier(int dirfd, const char *path, uid_t uid, int create)
Definition afppasswd.c:637
#define OPT_ADDUSER
Definition afppasswd.c:60
#define OPT_FORCE
Definition afppasswd.c:59
#define SRP_PASSWDLEN
Definition afppasswd.c:72
#define FORMAT_LEN
Definition afppasswd.c:70
static int srp_compute_verifier(const char *username, const char *password, const unsigned char *salt, unsigned char *v_out)
Definition afppasswd.c:492
static void srp_encode_hex(char *out_hex, const unsigned char *salt, const unsigned char *verifier)
Definition afppasswd.c:563
#define OPT_MIGRATE
Definition afppasswd.c:63
static int randnum_open_keyfile(const char *path, int *keyfd_out)
Definition afppasswd.c:305
static int open_credential_file(const char *path, int access_mode)
Definition afppasswd.c:165
#define PASSWDLEN
Definition afppasswd.c:76
#define AFPPASSWD_OPTSTRING
Definition afppasswd.c:78
static int valid_hex_or_disabled(const char *field, size_t len)
Definition afppasswd.c:1138
static int open_credential_for_replacement(const char *path)
Definition afppasswd.c:215
#define PASSWD_ILLEGAL
Definition afppasswd.c:66
static int randnum_write_keyfile(const char *keypath)
Definition afppasswd.c:333
static int convert_passwd(char *passwd_buf, char *newpwd, const int keyfd)
Definition afppasswd.c:417
static int randnum_ensure_keyfile(const char *path, int flags)
Definition afppasswd.c:369
static int create_file(const char *path, uid_t minuid)
Definition afppasswd.c:1404
#define HEXPASSWDLEN
Definition afppasswd.c:75
static int validate_opened_file(int fd, const char *path)
Definition afppasswd.c:128
static char buf[MAXPATHLEN+1]
Definition afppasswd.c:85
static int randnum_read_keyfd(int keyfd, uint8_t key[DES_KEY_SZ], const char *keypath)
Definition afppasswd.c:256
static int find_append_position(FILE *fp, const char *path, off_t *pos)
Definition afppasswd.c:184
static int open_srp_verifier_directory(const char *path)
Definition afppasswd.c:581
static int disable_srp_verifier(const char *path, const char *name, uid_t uid)
Definition afppasswd.c:973
static int update_passwd(const char *path, const char *name, int flags, const char *pass)
Definition afppasswd.c:1171
static const unsigned char hextable[]
Definition afppasswd.c:86
static int create_srp_directory(const char *path, uid_t minuid)
Definition afppasswd.c:1056
#define DES_KEY_SZ
Definition afppasswd.c:50
static int valid_randnum_record(const char *fields)
Definition afppasswd.c:1159
static int randnum_make_keypath(const char *path, char *keypath, size_t keypath_size)
Definition afppasswd.c:240
#define OPT_RANDNUM
Definition afppasswd.c:62
#define FORMAT
Definition afppasswd.c:69
#define OPT_CREATE
Definition afppasswd.c:58
#define OPT_DISABLE
Definition afppasswd.c:64
#define OPT_ISROOT
Definition afppasswd.c:57
static int update_srp_passwd(const char *path, const char *name, uid_t uid, int flags, const char *pass)
Definition afppasswd.c:690
static int validate_srp_verifier_file(int fd, uid_t uid, const char *path, int administrative)
Definition afppasswd.c:618
#define UID_START
Definition afppasswd.c:74
static int initialize_libgcrypt(void)
Definition afppasswd.c:93
static int srp_uid_filename(uid_t uid, char *name, size_t size)
Definition afppasswd.c:612
static int parse_minimum_uid(const char *value, uid_t *uid)
Definition afppasswd.c:106
#define OPT_NOCRACK
Definition afppasswd.c:61
int afppasswd_migrate_srp(const char *path, uid_t administrator_uid)
Definition afppasswd_migrate.c:628
void explicit_bzero(void *s, size_t n)
Definition explicit_bzero.c:32
size_t strlcat(char *, const char *, size_t)
Definition strlcpy.c:59
size_t strlcpy(char *, const char *, size_t)
Definition strlcpy.c:36
size_t strnlen(const char *s, size_t n)
Definition misc.c:19
#define _PATH_CRACKLIB
Definition config.h:483
int atalk_ct_memcmp(const void *, const void *, size_t)
Constant-time memory equality check.
Definition constant_time.c:27
#define key
Definition hash.c:51
#define unlock(fd, offset, whence, len)
unlock a file
Definition include/atalk/util.h:158
#define S_ISDIR(s)
Definition megatron.h:96
#define SRP_HEX_V_LEN
Definition srp.h:19
#define SRP_USERNAME_MAX_LEN
Definition srp.h:14
#define SRP_NBYTES
Definition srp.h:11
#define SRP_HEX_SALT_LEN
Definition srp.h:18
#define SRP_FIELDS_LEN
Definition srp.h:21
#define SRP_SALT_LEN
Definition srp.h:10
#define SRP_SHA1_LEN
Definition srp.h:12
#define SRP_DISABLED_CHAR
Definition srp.h:15
#define SRP_FORMAT_LEN
Definition srp.h:23
Definition include/atalk/directory.h:140
#define fchown
Definition test_afppasswd.c:45
#define setpwent
Definition test_afppasswd.c:49
#define fchmod
Definition test_afppasswd.c:46
#define getpwent
Definition test_afppasswd.c:50
#define fstat
Definition test_afppasswd.c:44
#define fsync
Definition test_afppasswd.c:48
#define endpwent
Definition test_afppasswd.c:51
#define fcntl
Definition test_afppasswd.c:47
#define main
Definition test_afppasswd.c:721
#define write
Definition test_migrate.c:41
#define getpwnam
Definition test_migrate.c:40
#define UAM_NEED_LIBGCRYPT_VERSION
Definition uam.h:24
#define unhex(x)
Definition uams_srp.c:220
#define NULL
Definition utf8util.c:47