1139823Simp/*	$OpenBSD: skeyinit.c,v 1.75 2019/06/28 13:35:03 deraadt Exp $	*/
21541Srgrimes
31541Srgrimes/* OpenBSD S/Key (skeyinit.c)
41541Srgrimes *
51541Srgrimes * Authors:
61541Srgrimes *          Neil M. Haller <nmh@thumper.bellcore.com>
71541Srgrimes *          Philip R. Karn <karn@chicago.qualcomm.com>
81541Srgrimes *          John S. Walden <jsw@thumper.bellcore.com>
91541Srgrimes *          Scott Chasin <chasin@crimelab.com>
101541Srgrimes *          Todd C. Miller <millert@openbsd.org>
111541Srgrimes *
121541Srgrimes * S/Key initialization and seed update
131541Srgrimes */
141541Srgrimes
151541Srgrimes#include <sys/stat.h>
161541Srgrimes
171541Srgrimes#include <ctype.h>
181541Srgrimes#include <err.h>
191541Srgrimes#include <errno.h>
201541Srgrimes#include <fcntl.h>
211541Srgrimes#include <pwd.h>
221541Srgrimes#include <readpassphrase.h>
231541Srgrimes#include <stdio.h>
241541Srgrimes#include <stdlib.h>
251541Srgrimes#include <string.h>
261541Srgrimes#include <syslog.h>
271541Srgrimes#include <time.h>
281541Srgrimes#include <unistd.h>
2985052Sru#include <limits.h>
3050477Speter#include <utmp.h>
311541Srgrimes
321541Srgrimes#include <skey.h>
332168Spaul#include <bsd_auth.h>
342168Spaul
352168Spaul#ifndef SKEY_NAMELEN
361541Srgrimes#define SKEY_NAMELEN    4
371541Srgrimes#endif
388876Srgrimes
391541Srgrimesvoid	usage(void);
401541Srgrimesvoid	secure_mode(int *, char *, char *, size_t, char *, size_t);
411541Srgrimesvoid	normal_mode(char *, int, char *, char *);
421541Srgrimesvoid	enable_db(int);
431541Srgrimes
441541Srgrimesint
451541Srgrimesmain(int argc, char **argv)
461541Srgrimes{
471541Srgrimes	int     rval, i, l, n, defaultsetup, rmkey, hexmode, enable;
481541Srgrimes	char	hostname[HOST_NAME_MAX+1];
491541Srgrimes	char	seed[SKEY_MAX_SEED_LEN + 1];
501541Srgrimes	char    buf[256], key[SKEY_BINKEY_SIZE], filename[PATH_MAX], *ht;
511541Srgrimes	char    lastc, *p, *auth_type;
521541Srgrimes	const char *errstr;
531541Srgrimes	struct skey skey;
541541Srgrimes	struct passwd *pp;
551541Srgrimes
561541Srgrimes	n = rmkey = hexmode = enable = 0;
57122922Sandre	defaultsetup = 1;
58122922Sandre	ht = auth_type = NULL;
59122922Sandre
60122922Sandre	for (i = 1; i < argc && argv[i][0] == '-' && strcmp(argv[i], "--");) {
61122922Sandre		if (argv[i][2] == '\0') {
62122922Sandre			/* Single character switch */
631541Srgrimes			switch (argv[i][1]) {
641541Srgrimes			case 'a':
651541Srgrimes				if (argv[++i] == NULL || argv[i][0] == '\0')
661541Srgrimes					usage();
671541Srgrimes				auth_type = argv[i];
6813765Smpp				break;
6913765Smpp			case 's':
701541Srgrimes				defaultsetup = 0;
711541Srgrimes				if (auth_type == NULL)
721541Srgrimes					auth_type = "skey";
731541Srgrimes				break;
745791Swollman			case 'x':
751541Srgrimes				hexmode = 1;
761541Srgrimes				break;
771541Srgrimes			case 'r':
781541Srgrimes				rmkey = 1;
791541Srgrimes				break;
801541Srgrimes			case 'n':
811541Srgrimes				if (argv[++i] == NULL || argv[i][0] == '\0')
821541Srgrimes					usage();
831541Srgrimes				n = strtonum(argv[i], 1, SKEY_MAX_SEQ - 1, &errstr);
841541Srgrimes				if (errstr)
85183200Szec					errx(1, "count must be > 0 and < %d",
86183200Szec					     SKEY_MAX_SEQ);
87183200Szec				break;
88183200Szec			case 'D':
89183200Szec				enable = -1;
90183200Szec				break;
91183200Szec			case 'E':
92183200Szec				enable = 1;
93183200Szec				break;
94183200Szec			default:
95183200Szec				usage();
96183200Szec			}
97183200Szec		} else {
98183200Szec			/* Multi character switches are hash types */
99183200Szec			if ((ht = skey_set_algorithm(&argv[i][1])) == NULL) {
100183200Szec				warnx("Unknown hash algorithm %s", &argv[i][1]);
101183200Szec				usage();
102183200Szec			}
103183200Szec		}
104183200Szec		i++;
105183200Szec	}
106178888Sjulian	argv += i;
107178888Sjulian	argc -= i;
108178888Sjulian
1091541Srgrimes	if (argc > 1 || (enable && argc))
1105833Sbde		usage();
1115833Sbde
1125833Sbde	/* Handle -D and -E */
1135833Sbde	if (enable) {
1145833Sbde		enable_db(enable);
1151541Srgrimes		exit(0);
1161541Srgrimes	}
1171541Srgrimes
1181541Srgrimes	if (getuid() != 0) {
1191541Srgrimes		if (pledge("stdio rpath wpath cpath fattr flock tty proc exec "
1201541Srgrimes		    "getpw", NULL) == -1)
1211541Srgrimes			err(1, "pledge");
1221541Srgrimes
1231541Srgrimes		if ((pp = getpwuid(getuid())) == NULL)
124178167Sqingli			err(1, "no user with uid %u", getuid());
125178167Sqingli
1261541Srgrimes		if (argc == 1) {
127178167Sqingli			char me[UT_NAMESIZE + 1];
1281541Srgrimes
1291541Srgrimes			(void)strlcpy(me, pp->pw_name, sizeof me);
130128454Sluigi			if ((pp = getpwnam(argv[0])) == NULL)
131128454Sluigi				errx(1, "User unknown: %s", argv[0]);
132128454Sluigi			if (strcmp(pp->pw_name, me) != 0)
133128454Sluigi				errx(1, "Permission denied.");
134128454Sluigi		}
135132780Skan	} else {
136132780Skan		if (pledge("stdio rpath wpath cpath fattr flock tty getpw id",
1371541Srgrimes		    NULL) == -1)
138186119Sqingli			err(1, "pledge");
139186119Sqingli
1401541Srgrimes		if (argc == 1) {
141122922Sandre			if ((pp = getpwnam(argv[0])) == NULL) {
142127828Sluigi				static struct passwd _pp;
143178888Sjulian
144120727Ssam				_pp.pw_name = argv[0];
145120727Ssam				pp = &_pp;
146120727Ssam				warnx("Warning, user unknown: %s", argv[0]);
147120727Ssam			} else {
1481541Srgrimes				/* So the file ends up owned by the proper ID */
1491541Srgrimes				if (setresuid(-1, pp->pw_uid, -1) != 0)
1501541Srgrimes					errx(1, "unable to change uid to %u",
1511541Srgrimes					    pp->pw_uid);
1521541Srgrimes			}
1531541Srgrimes		} else if ((pp = getpwuid(0)) == NULL)
1541541Srgrimes			err(1, "no user with uid 0");
1551541Srgrimes
1561541Srgrimes		if (pledge("stdio rpath wpath cpath fattr flock tty", NULL)
1571541Srgrimes		    == -1)
1581541Srgrimes			err(1, "pledge");
1591541Srgrimes	}
1601541Srgrimes
1611541Srgrimes	switch (skey_haskey(pp->pw_name)) {
1621541Srgrimes	case -1:
1631541Srgrimes		if (errno == ENOENT || errno == EPERM)
1644104Swollman			errx(1, "S/Key disabled");
1654104Swollman		else
1661541Srgrimes			err(1, "cannot open database");
1671541Srgrimes		break;
1681541Srgrimes	case 0:
1691541Srgrimes		/* existing user */
1701541Srgrimes		break;
1711541Srgrimes	case 1:
1721541Srgrimes		if (!defaultsetup && strcmp(auth_type, "skey") == 0) {
17386764Sjlemon			fprintf(stderr,
174186119Sqingli"You must authenticate yourself before using S/Key for the first time.  In\n"
1751541Srgrimes"secure mode this is normally done via an existing S/Key key.  However, since\n"
176186119Sqingli"you do not have an entry in the S/Key database you will have to specify an\n"
177186500Sqingli"alternate authentication type via the `-a' flag, e.g.\n"
1781541Srgrimes"    \"skeyinit -s -a passwd\"\n\n"
1791541Srgrimes"Note that entering a plaintext password over a non-secure link defeats the\n"
1801541Srgrimes"purpose of using S/Key in the fist place.\n");
1811541Srgrimes			exit(1);
1821541Srgrimes		}
183122921Sandre		break;
184122921Sandre	}
185122921Sandre
186122921Sandre	if (getuid() != 0) {
187122921Sandre		if ((pp = pw_dup(pp)) == NULL)
188186119Sqingli			err(1, NULL);
1895099Swollman		if (!auth_userokay(pp->pw_name, auth_type, NULL, NULL))
19018839Swollman			errx(1, "Password incorrect");
1916245Swollman	}
19215652Swollman
19315652Swollman	if (pledge("stdio rpath wpath cpath fattr flock tty", NULL) == -1)
19415652Swollman		err(1, "pledge");
19515652Swollman
196185747Skmacy	/* Build up a default seed based on the hostname and some randomness */
1971541Srgrimes	if (gethostname(hostname, sizeof(hostname)) == -1)
198156750Sandre		err(1, "gethostname");
199156750Sandre	for (i = 0, p = seed; hostname[i] && i < SKEY_NAMELEN; i++) {
200156750Sandre		if (isalnum((unsigned char)hostname[i]))
201156750Sandre			*p++ = tolower((unsigned char)hostname[i]);
202156750Sandre	}
2031541Srgrimes	for (i = 0; i < 5; i++)
2041541Srgrimes		*p++ = arc4random_uniform(10) + '0';
2051541Srgrimes	*p = '\0';
2061541Srgrimes
2071541Srgrimes	/*
2081541Srgrimes	 * Lookup and lock the record we are about to modify.
2091541Srgrimes	 * If this is a new entry this will prevent other users
2101541Srgrimes	 * from appending new entries (and clobbering ours).
2111541Srgrimes	 */
2121541Srgrimes	rval = skeylookup(&skey, pp->pw_name);
2131541Srgrimes	switch (rval) {
2141541Srgrimes		case -1:
2151541Srgrimes			err(1, "cannot open database");
2161541Srgrimes			break;
2171541Srgrimes		case 0:
2181541Srgrimes			/* remove user if asked to do so */
2191541Srgrimes			if (rmkey) {
2201541Srgrimes				if (snprintf(filename, sizeof(filename),
2211541Srgrimes				    "%s/%s", _PATH_SKEYDIR, pp->pw_name)
2221541Srgrimes				    >= sizeof(filename))
2231541Srgrimes					errc(1, ENAMETOOLONG,
2241541Srgrimes					    "Cannot remove S/Key entry");
2251541Srgrimes				if (unlink(filename) != 0)
226156750Sandre					err(1, "Cannot remove S/Key entry");
227156750Sandre				printf("S/Key entry for %s removed.\n",
2281541Srgrimes				    pp->pw_name);
2291541Srgrimes				exit(0);
2301541Srgrimes			}
2311541Srgrimes
2325791Swollman			(void)printf("[Updating %s with %s]\n", pp->pw_name,
2331541Srgrimes			    ht ? ht : skey_get_algorithm());
23451252Sru			(void)printf("Old seed: [%s] %s\n",
23551252Sru				     skey_get_algorithm(), skey.seed);
23651252Sru
2371541Srgrimes			/*
2381541Srgrimes			 * Sanity check old seed.
2391541Srgrimes			 */
2401541Srgrimes			l = strlen(skey.seed);
2411541Srgrimes			for (p = skey.seed; *p; p++) {
2421541Srgrimes				if (isalpha((unsigned char)*p)) {
2431541Srgrimes					if (isupper((unsigned char)*p))
2441541Srgrimes						*p = tolower((unsigned char)*p);
2451541Srgrimes				} else if (!isdigit((unsigned char)*p)) {
2461541Srgrimes					memmove(p, p + 1, l - (p - skey.seed));
2471541Srgrimes					l--;
2481541Srgrimes				}
2491541Srgrimes			}
2501541Srgrimes
25121666Swollman			/* If the seed ends in 0-8 just add one.  */
25221666Swollman			if (l > 0) {
25389498Sru				lastc = skey.seed[l - 1];
254136155Ssam				if (isdigit((unsigned char)lastc) &&
2551541Srgrimes				    lastc != '9') {
25651252Sru					(void)strlcpy(seed, skey.seed,
25751252Sru					    sizeof seed);
25851252Sru					seed[l - 1] = lastc + 1;
2591541Srgrimes				}
2601541Srgrimes				if (isdigit((unsigned char)lastc) &&
26151252Sru				    lastc == '9' && l < 16) {
2621541Srgrimes					(void)strlcpy(seed, skey.seed,
2631541Srgrimes					    sizeof seed);
2641541Srgrimes					seed[l - 1] = '0';
2651541Srgrimes					seed[l] = '0';
2661541Srgrimes					seed[l + 1] = '\0';
2671541Srgrimes				}
2681541Srgrimes			}
26951252Sru			break;
2701541Srgrimes		case 1:
2711541Srgrimes			if (rmkey)
2721541Srgrimes				errx(1, "You have no entry to remove.");
2731541Srgrimes			(void)printf("[Adding %s with %s]\n", pp->pw_name,
2741541Srgrimes			    ht ? ht : skey_get_algorithm());
2751541Srgrimes			if (snprintf(filename, sizeof(filename), "%s/%s",
2761541Srgrimes			    _PATH_SKEYDIR, pp->pw_name) >= sizeof(filename))
2771541Srgrimes				errc(1, ENAMETOOLONG,
2781541Srgrimes				    "Cannot create S/Key entry");
2791541Srgrimes			if ((l = open(filename,
2801541Srgrimes			    O_RDWR | O_NONBLOCK | O_CREAT | O_TRUNC |O_NOFOLLOW,
2811541Srgrimes			    S_IRUSR | S_IWUSR)) == -1 ||
2821541Srgrimes			    flock(l, LOCK_EX) != 0 ||
2831541Srgrimes			    (skey.keyfile = fdopen(l, "r+")) == NULL)
2841541Srgrimes				err(1, "Cannot create S/Key entry");
2851541Srgrimes			break;
2861541Srgrimes	}
2871541Srgrimes	if (fchown(fileno(skey.keyfile), pp->pw_uid, -1) != 0 ||
2881541Srgrimes	    fchmod(fileno(skey.keyfile), S_IRUSR | S_IWUSR) != 0)
2891541Srgrimes		err(1, "can't set owner/mode for %s", pp->pw_name);
2901541Srgrimes	if (defaultsetup && n == 0)
2911541Srgrimes		n = 100;
2921541Srgrimes
2931541Srgrimes	/* Set hash type if asked to */
2941541Srgrimes	if (ht && strcmp(ht, skey_get_algorithm()) != 0)
2951541Srgrimes		skey_set_algorithm(ht);
29685074Sru
29785074Sru	alarm(180);
29885074Sru	if (!defaultsetup)
2991541Srgrimes		secure_mode(&n, key, seed, sizeof seed, buf, sizeof(buf));
3001541Srgrimes	else
301128185Sluigi		normal_mode(pp->pw_name, n, key, seed);
302128185Sluigi	alarm(0);
303128185Sluigi
304128185Sluigi	/* XXX - why use malloc here? */
305128185Sluigi	if ((skey.val = malloc(16 + 1)) == NULL)
306128185Sluigi		err(1, "Can't allocate memory");
307128185Sluigi	btoa8(skey.val, key);
308128185Sluigi
309128185Sluigi	(void)fseek(skey.keyfile, 0L, SEEK_SET);
310128185Sluigi	(void)fprintf(skey.keyfile, "%s\n%s\n%04d\n%s\n%s\n",
311128185Sluigi	    pp->pw_name, skey_get_algorithm(), n, seed, skey.val);
312128185Sluigi	(void)fclose(skey.keyfile);
31355205Speter
314117752Shsu	(void)printf("\nID %s skey is otp-%s %d %s\n", pp->pw_name,
315120727Ssam	    skey_get_algorithm(), n, seed);
316120727Ssam	(void)printf("Next login password: %s\n\n",
317120727Ssam	    hexmode ? put8(buf, key) : btoe(buf, key));
318174934Smux	exit(0);
319120727Ssam}
320120727Ssam
321120727Ssamvoid
322117752Shsusecure_mode(int *count, char *key, char *seed, size_t seedlen,
323122334Ssam    char *buf, size_t bufsiz)
324122334Ssam{
325122334Ssam	char *p, newseed[SKEY_MAX_SEED_LEN + 2];
326186119Sqingli	const char *errstr;
327122334Ssam	int i, n = *count;
328150130Sandre
329182801Sjulian	(void)puts("You need the 6 words generated from the \"skey\" command.");
330122334Ssam	if (n == 0) {
331122334Ssam		for (i = 0; ; i++) {
332122334Ssam			if (i >= 2)
333186119Sqingli				exit(1);
334122334Ssam
335150130Sandre			(void)printf("Enter sequence count from 1 to %d: ",
336122334Ssam			    SKEY_MAX_SEQ);
337122334Ssam			(void)fgets(buf, bufsiz, stdin);
338183017Sjulian			clearerr(stdin);
339183017Sjulian			rip(buf);
340183017Sjulian			n = strtonum(buf, 1, SKEY_MAX_SEQ-1, &errstr);
341183017Sjulian			if (!errstr)
342183017Sjulian				break;	/* Valid range */
343183017Sjulian			fprintf(stderr,
344183017Sjulian			    "ERROR: Count must be between 1 and %d\n",
345183017Sjulian			    SKEY_MAX_SEQ - 1);
346183017Sjulian		}
347182801Sjulian		*count= n;
348122334Ssam	}
349183017Sjulian
350183017Sjulian	for (i = 0; ; i++) {
351183017Sjulian		if (i >= 2)
3521541Srgrimes			exit(1);
353183017Sjulian
354183017Sjulian		(void)printf("Enter new seed [default %s]: ", seed);
355183017Sjulian		(void)fgets(newseed, sizeof(newseed), stdin); /* XXX */
356183017Sjulian		clearerr(stdin);
357183017Sjulian		rip(newseed);
358183017Sjulian		if (strlen(newseed) > SKEY_MAX_SEED_LEN) {
359183017Sjulian			(void)fprintf(stderr, "ERROR: Seed must be between 1 "
360183017Sjulian			    "and %d characters in length\n", SKEY_MAX_SEED_LEN);
361183017Sjulian			continue;
362183017Sjulian		}
363183017Sjulian		for (p = newseed; *p; p++) {
364183017Sjulian			if (isspace((unsigned char)*p)) {
365183017Sjulian				(void)fputs("ERROR: Seed must not contain "
366183017Sjulian				    "any spaces\n", stderr);
367183017Sjulian				break;
368183017Sjulian			} else if (isalpha((unsigned char)*p)) {
369178898Sjulian				if (isupper((unsigned char)*p))
3701541Srgrimes					*p = tolower((unsigned char)*p);
37121666Swollman			} else if (!isdigit((unsigned char)*p)) {
37221666Swollman				(void)fputs("ERROR: Seed must be purely "
373136155Ssam				    "alphanumeric\n", stderr);
37492725Salfred				break;
37592725Salfred			}
37692725Salfred		}
37792725Salfred		if (*p == '\0')
37892725Salfred			break;  /* Valid seed */
37992725Salfred	}
380128621Sluigi	if (newseed[0] != '\0')
381128621Sluigi		(void)strlcpy(seed, newseed, seedlen);
382128621Sluigi
383128621Sluigi	for (i = 0; ; i++) {
384128621Sluigi		if (i >= 2)
385128621Sluigi			exit(1);
386128621Sluigi
387128621Sluigi		(void)printf("otp-%s %d %s\nS/Key access password: ",
388128621Sluigi			     skey_get_algorithm(), n, seed);
389128621Sluigi		(void)fgets(buf, bufsiz, stdin);
390128621Sluigi		clearerr(stdin);
391128621Sluigi		rip(buf);
392128621Sluigi		backspace(buf);
393178888Sjulian
394178888Sjulian		if (buf[0] == '?') {
395183013Sjulian			(void)puts("Enter 6 words from secure S/Key calculation.");
396178888Sjulian			continue;
397178888Sjulian		} else if (buf[0] == '\0')
398178888Sjulian			exit(1);
399178888Sjulian
400128621Sluigi		if (etob(key, buf) == 1 || atob8(key, buf) == 0)
401128621Sluigi			break;	/* Valid format */
402120727Ssam		(void)fputs("ERROR: Invalid format - try again with the 6 words.\n",
40392725Salfred		    stderr);
40492725Salfred	}
40592725Salfred}
406120727Ssam
40792725Salfredvoid
40892725Salfrednormal_mode(char *username, int n, char *key, char *seed)
409174559Skmacy{
410178888Sjulian	int i, nn;
411178888Sjulian	char passwd[SKEY_MAX_PW_LEN+2], key2[SKEY_BINKEY_SIZE];
412178888Sjulian
413178888Sjulian	/* Get user's secret passphrase */
414178888Sjulian	for (i = 0; ; i++) {
415178888Sjulian		if (i > 2)
416178888Sjulian			errx(1, "S/Key entry not updated");
417178888Sjulian
418178888Sjulian		if (readpassphrase("Enter new secret passphrase: ", passwd,
419178888Sjulian		    sizeof(passwd), 0) == NULL || passwd[0] == '\0')
420178888Sjulian			exit(1);
421178888Sjulian
422178888Sjulian		if (strlen(passwd) < SKEY_MIN_PW_LEN) {
423178888Sjulian			(void)fprintf(stderr,
424178888Sjulian			    "ERROR: Your passphrase must be at least %d "
425178888Sjulian			    "characters long.\n", SKEY_MIN_PW_LEN);
426178888Sjulian			continue;
427178888Sjulian		} else if (strcmp(passwd, username) == 0) {
428174559Skmacy			(void)fputs("ERROR: Your passphrase may not be the "
429174703Skmacy			    "same as your user name.\n", stderr);
430174703Skmacy			continue;
431174703Skmacy		} else if (strspn(passwd, "abcdefghijklmnopqrstuvwxyz") ==
432174703Skmacy		    strlen(passwd)) {
4331541Srgrimes			(void)fputs("ERROR: Your passphrase must contain more "
4342168Spaul			    "than just lower case letters.\nWhitespace, "
4352168Spaul			    "numbers, and punctuation are suggested.\n",
436			    stderr);
437			continue;
438		} else if (strlen(passwd) > 63) {
439			(void)fprintf(stderr, "WARNING: Your passphrase is "
440			    "longer than the recommended maximum length of 63\n");
441		}
442		/* XXX - should check for passphrase that is really too long */
443
444		/* Crunch seed and passphrase into starting key */
445		nn = keycrunch(key, seed, passwd);
446		explicit_bzero(passwd, sizeof(passwd));
447		if (nn != 0)
448			err(2, "key crunch failed");
449
450		if (readpassphrase("Again secret passphrase: ", passwd,
451		    sizeof(passwd), 0) == NULL || passwd[0] == '\0')
452			exit(1);
453
454		/* Crunch seed and passphrase into starting key */
455		nn = keycrunch(key2, seed, passwd);
456		explicit_bzero(passwd, sizeof(passwd));
457		if (nn != 0)
458			err(2, "key crunch failed");
459
460		if (memcmp(key, key2, sizeof(key2)) == 0)
461			break;
462
463		(void)fputs("Passphrases do not match.\n", stderr);
464	}
465
466	nn = n;
467	while (nn-- != 0)
468		f(key);
469}
470
471void
472enable_db(int op)
473{
474	if (op == 1) {
475		/* enable */
476		if (mkdir(_PATH_SKEYDIR, 01730) != 0 && errno != EEXIST)
477			err(1, "can't mkdir %s", _PATH_SKEYDIR);
478		if (chown(_PATH_SKEYDIR, geteuid(), getegid()) != 0)
479			err(1, "can't chown %s", _PATH_SKEYDIR);
480		if (chmod(_PATH_SKEYDIR, 01730) != 0)
481			err(1, "can't chmod %s", _PATH_SKEYDIR);
482	} else {
483		/* disable */
484		if (chmod(_PATH_SKEYDIR, 0) != 0 && errno != ENOENT)
485			err(1, "can't chmod %s", _PATH_SKEYDIR);
486	}
487}
488
489void
490usage(void)
491{
492	extern char *__progname;
493
494	(void)fprintf(stderr, "usage: %s [-DErsx] [-a auth-type] [-n count]"
495	    "\n\t[-md5 | -rmd160 | -sha1] [user]\n", __progname);
496	exit(1);
497}
498