xref: /illumos-gate/usr/src/cmd/gss/gsscred/gsscred.c (revision 55fea89d)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 1997-2002 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  *  gsscred utility
29  *  Manages mapping between a security principal name and unix uid
30  */
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <pwd.h>
35 #include <unistd.h>
36 #include <string.h>
37 #include <gssapi/gssapi_ext.h>
38 #include "gsscred.h"
39 
40 #define	MAX_STR_LEN	1024
41 
42 
43 /*
44  * Internal Functions
45  */
46 static void usage(void);
47 static void addUser(const char *name, const char *oid, const char *userUid,
48 		const char *userComment, const char *userMech);
49 static int file_listUsers(const gss_OID mechOid, const char *userUid,
50 		char **errDetails);
51 static int listUsers(const char *name, const char *nameTypeOid,
52 		const char *uid, const char *mechOid);
53 static int file_removeUsers(const gss_OID mechOid, const char *userUid,
54 		char **errDetails);
55 static int removeUsers(const char *name, const char *nameTypeOid,
56 		const char *uid, const char *mechOid);
57 
58 /*
59  * Global variables
60  */
61 static int tableSource;
62 static char *PROG_NAME = NULL;
63 
64 int
65 main(int argc, char *args[])
66 {
67 	char *userName = NULL, *nameTypeOID = NULL,
68 		*uid = NULL, *comment = NULL, *mech = NULL,
69 		operation = '0';
70 	int c, errflag = 0;
71 	extern char *optarg;
72 
73 	PROG_NAME = *args;
74 
75 	/* set locale and domain for internationalization */
76 	setlocale(LC_ALL, "");
77 	textdomain(TEXT_DOMAIN);
78 
79 	if (argc < 2)
80 		usage();
81 
82 	/* Process the input arguments */
83 	while ((c = getopt(argc, args, "arln:o:u:m:c:")) != EOF) {
84 
85 		switch (c) {
86 		case 'n':
87 			userName = optarg;
88 			break;
89 
90 		case 'o':
91 			nameTypeOID = optarg;
92 			break;
93 
94 		case 'u':
95 			uid = optarg;
96 			break;
97 
98 		case 'm':
99 			mech = optarg;
100 			break;
101 
102 		case 'c':
103 			comment = optarg;
104 			break;
105 
106 		case 'a':
107 		case 'r':
108 		case 'l':
109 			operation = c;
110 			errflag++;
111 			if (errflag > 1)
112 				usage();
113 			break;
114 
115 		default:
116 			usage();
117 		}
118 	}
119 
120 	/* determine which back-end to use as the gsscred store */
121 	tableSource = gsscred_read_config_file();
122 
123 	/* perform the requested operation */
124 	switch (operation) {
125 		case 'a':
126 			addUser(userName, nameTypeOID, uid, comment, mech);
127 			break;
128 
129 		case 'r':
130 			removeUsers(userName, nameTypeOID, uid, mech);
131 			break;
132 
133 		case 'l':
134 			listUsers(userName, nameTypeOID, uid, mech);
135 			break;
136 
137 		default:
138 			usage();
139 	}
140 	fprintf(stdout, "\n");
141 	return (0);
142 }  /* main */
143 
144 /*
145  * Handles the addition of users to the gsscred table.
146  */
147 static void
148 addUser(const char *name, const char *nameOidStr,
149 	    const char *userUid, const char *userComment,
150 	    const char *mechOidStr)
151 {
152 	gss_OID mechOid;
153 	gss_buffer_desc fullName = GSS_C_EMPTY_BUFFER,
154 		hexBufDesc = GSS_C_EMPTY_BUFFER,
155 		hexMechOid = GSS_C_EMPTY_BUFFER;
156 	char comment[MAX_STR_LEN+1], hexBuf[MAX_STR_LEN+MAX_STR_LEN+1],
157 		hexMechOidBuf[MAX_STR_LEN+1], *commentPtr = NULL,
158 		*errDetail = NULL, uidStr[256], *uidPtr;
159 	struct passwd *aUser;
160 	OM_uint32 minor;
161 	int count = 0, retCode;
162 
163 	hexMechOid.length = MAX_STR_LEN;
164 	hexMechOid.value = (void*)hexMechOidBuf;
165 
166 	/* addition of users can only be performed by super users */
167 	if (getuid()) {
168 		fprintf(stderr,
169 			gettext("\nUser addition requires"
170 				" root privileges."));
171 		return;
172 	}
173 
174 	/* the mechanism OID is required */
175 	if (mechOidStr == NULL) {
176 		fprintf(stderr, gettext("\nUnspecified mechanism."));
177 		usage();
178 	}
179 
180 	/* Convert from string mechanism Oid to ASN.1 oid and then hex */
181 	if (__gss_mech_to_oid(mechOidStr, &mechOid) != GSS_S_COMPLETE) {
182 		fprintf(stderr,
183 			gettext("\nInvalid mechanism specified [%s]."),
184 			mechOidStr);
185 		return;
186 	}
187 
188 	hexBufDesc.length = mechOid->length;
189 	hexBufDesc.value = mechOid->elements;
190 
191 	if (!gsscred_AsHex(&hexBufDesc, &hexMechOid)) {
192 		fprintf(stderr,
193 			gettext("\nInternal error.  "
194 				"Conversion to hex failed."));
195 		return;
196 	}
197 
198 	/*
199 	 * if the name is specified, then do single addition.
200 	 * Might have to look up the uid.
201 	 */
202 	if (name != NULL) {
203 		hexBufDesc.length = sizeof (hexBuf);
204 		hexBufDesc.value = hexBuf;
205 
206 		/* build the name as needed */
207 		if (!gsscred_MakeName(mechOid, name, nameOidStr, &fullName)) {
208 			fprintf(stderr,
209 				gettext("\nError adding user [%s]."), name);
210 			return;
211 		}
212 
213 		/* convert it to hex */
214 		if (!gsscred_AsHex(&fullName, &hexBufDesc)) {
215 			gss_release_buffer(&minor, &fullName);
216 			fprintf(stderr,
217 				gettext("\nInternal error.  "
218 					"Conversion to hex failed."));
219 			return;
220 		}
221 
222 		/* might require the lookup of the uid if one not specified */
223 		if (userUid == NULL) {
224 
225 			if ((aUser = getpwnam(name)) == NULL) {
226 				fprintf(stderr,
227 					gettext("\nUnable to obtain password"
228 						" information for [%s]."),
229 					name);
230 				gss_release_buffer(&minor, &fullName);
231 				return;
232 			}
233 			sprintf(uidStr, "%ld", aUser->pw_uid);
234 			uidPtr = uidStr;
235 		}
236 		else
237 			uidPtr = (char *)userUid;
238 
239 		if (userComment == NULL) {
240 			sprintf(comment, "%s, %s", name, mechOidStr);
241 			commentPtr = comment;
242 		} else
243 			commentPtr = (char *)userComment;
244 
245 		if (tableSource == GSSCRED_FLAT_FILE)
246 			retCode = file_addGssCredEntry(&hexBufDesc,
247 					uidPtr, commentPtr, &errDetail);
248 		else
249 			/* other backends (ldap, dss) coming soon */
250 			retCode	= 0;
251 
252 		if (!retCode) {
253 			fprintf(stderr, gettext("\nError adding user [%s]."),
254 				commentPtr);
255 
256 			if (errDetail) {
257 				fprintf(stderr, "\n%s\n", errDetail);
258 				free(errDetail);
259 				errDetail = NULL;
260 			}
261 		}
262 
263 		gss_release_buffer(&minor, &fullName);
264 		return;
265 	}
266 
267 	/*
268 	 * since no name specified, then we will load everyone from
269 	 * password table.  This means that -u and -o options are invalid.
270 	 * We just ignore it, but we could flag it as error.
271 	 */
272 	setpwent();
273 
274 	while ((aUser = getpwent()) != NULL) {
275 		hexBufDesc.length = sizeof (hexBuf);
276 		hexBufDesc.value = hexBuf;
277 
278 		if (!gsscred_MakeName(mechOid, aUser->pw_name,
279 			nameOidStr, &fullName)) {
280 			fprintf(stderr,
281 				gettext("\nError adding user [%s]."),
282 				aUser->pw_name);
283 			continue;
284 		}
285 
286 		if (!gsscred_AsHex(&fullName, &hexBufDesc)) {
287 			gss_release_buffer(&minor, &fullName);
288 			fprintf(stderr,
289 				gettext("\nInternal error.  "
290 					"Conversion to hex failed."));
291 			continue;
292 		}
293 
294 		sprintf(uidStr, "%ld", aUser->pw_uid);
295 		sprintf(comment, "%s, %s", aUser->pw_name, mechOidStr);
296 		if (tableSource == GSSCRED_FLAT_FILE)
297 			retCode = file_addGssCredEntry(&hexBufDesc,
298 					uidStr, comment, &errDetail);
299 		else
300 			retCode	= 0;
301 
302 		if (!retCode) {
303 			fprintf(stderr,
304 				gettext("\nError adding user [%s]."),
305 				comment);
306 
307 			if (errDetail) {
308 				fprintf(stderr, "\n%s\n", errDetail);
309 				free(errDetail);
310 				errDetail = NULL;
311 			}
312 		} else {
313 			count++;
314 			if ((count % 50) == 0)
315 				fprintf(stdout,
316 					gettext("\n[%d] users added..."),
317 					count);
318 		}
319 		gss_release_buffer(&minor, &fullName);
320 	}
321 	endpwent();
322 }  /* addUser */
323 
324 
325 /*
326  *  Handles the searching of the gsscred table.
327  */
328 static int listUsers(const char *name, const char *nameOidStr,
329 		const char *uidStr, const char *mechOidStr)
330 {
331 	GssCredEntry *entryPtr, *entryTmpPtr;
332 	char hexMech[256],
333 		hexName[(MAX_STR_LEN *2) + 1];
334 	gss_OID anOid = NULL, userMechOid = NULL;
335 	gss_OID_set mechSet = NULL;
336 	gss_buffer_desc inBufDesc = GSS_C_EMPTY_BUFFER,
337 		outBufDesc = GSS_C_EMPTY_BUFFER,
338 		searchName = GSS_C_EMPTY_BUFFER;
339 	int status = 1, numOfMechs, i;
340 	OM_uint32 minor;
341 	char *errDetails = NULL;
342 
343 	/* Do we need to convert the mechanism oid? */
344 	if (mechOidStr != NULL) {
345 
346 		if (__gss_mech_to_oid(mechOidStr, &userMechOid) !=
347 			GSS_S_COMPLETE) {
348 			fprintf(stderr,
349 				gettext("\nInvalid mechanism specified [%s]."),
350 				mechOidStr);
351 			return (0);
352 		}
353 		inBufDesc.length = userMechOid->length;
354 		inBufDesc.value = userMechOid->elements;
355 		outBufDesc.length = sizeof (hexMech);
356 		outBufDesc.value = hexMech;
357 
358 		if (!gsscred_AsHex(&inBufDesc, &outBufDesc)) {
359 			fprintf(stderr,
360 				gettext("\nInternal error.  "
361 					"Conversion to hex failed."));
362 			status = 0;
363 			goto cleanup;
364 		}
365 
366 	}	/* mechOidStr != NULL */
367 
368 	/* are we retrieving everyone ? or searching by mech ? */
369 	if ((name == NULL && uidStr == NULL && mechOidStr == NULL) ||
370 	    (name == NULL && uidStr == NULL)) {
371 
372 		if (tableSource == GSSCRED_FLAT_FILE) {
373 			file_listUsers(userMechOid, NULL, &errDetails);
374 
375 			if (errDetails) {
376 				fprintf(stderr,
377 					gettext("\nError searching gsscred"
378 						" table [%s]."),
379 					errDetails);
380 				free(errDetails);
381 				errDetails = NULL;
382 				return (0);
383 			}
384 			return (1);
385 		}
386 
387 	}
388 
389 	/* Are we searching by uid or uid and mech? */
390 	if (name == NULL && uidStr != NULL) {
391 
392 		if (tableSource == GSSCRED_FLAT_FILE)
393 			file_listUsers(userMechOid, uidStr, &errDetails);
394 		else {
395 			entryPtr = NULL;
396 			while (entryPtr != NULL) {
397 				fprintf(stdout, "\n%s\t%d\t%s",
398 					entryPtr->principal_name,
399 					entryPtr->unix_uid, entryPtr->comment);
400 				free(entryPtr->principal_name);
401 				free(entryPtr->comment);
402 				entryTmpPtr = entryPtr->next;
403 				free(entryPtr);
404 				entryPtr = entryTmpPtr;
405 			}
406 		}
407 
408 		/* check for any errors */
409 		if (errDetails) {
410 			fprintf(stderr,
411 				gettext("\nError searching gsscred table "
412 					"[%s]."),
413 				errDetails);
414 			free(errDetails);
415 			errDetails = NULL;
416 			status = 0;
417 		}
418 
419 		goto cleanup;
420 	}
421 
422 	/*
423 	 * We are searching by name;
424 	 * how many mechs must we check?
425 	 */
426 	if (mechOidStr == NULL) {
427 
428 		if (gss_indicate_mechs(&minor, &mechSet) != GSS_S_COMPLETE) {
429 			fprintf(stderr,
430 				gettext("\nInternal error.  "
431 					"GSS-API call failed."));
432 			return (0);
433 		}
434 		numOfMechs = mechSet->count;
435 	}
436 	else
437 		numOfMechs = 1;
438 
439 	/* now look through all the mechs searching */
440 	for (i = 0; i < numOfMechs; i++) {
441 
442 		if (mechOidStr == NULL) {
443 			anOid = &mechSet->elements[i];
444 			inBufDesc.length = anOid->length;
445 			inBufDesc.value = anOid->elements;
446 			outBufDesc.length = sizeof (hexMech);
447 			outBufDesc.value = hexMech;
448 
449 			if (!gsscred_AsHex(&inBufDesc, &outBufDesc))
450 				continue;
451 		} else
452 			anOid = userMechOid;
453 
454 		/* create a gss name */
455 		if (!gsscred_MakeName(anOid, name, nameOidStr, &outBufDesc))
456 			continue;
457 
458 		/* now convert it to hex, and find it */
459 		searchName.value = hexName;
460 		searchName.length = sizeof (hexName);
461 		status = gsscred_AsHex(&outBufDesc, &searchName);
462 		free(outBufDesc.value);
463 
464 		if (!status)
465 			continue;
466 
467 		if (tableSource == GSSCRED_FLAT_FILE)
468 			file_getGssCredEntry(&searchName, uidStr, &errDetails);
469 		else {
470 			entryPtr = NULL;  /* other backends coming soon */
471 			while (entryPtr != NULL) {
472 				fprintf(stdout, "\n%s\t%d\t%s",
473 					entryPtr->principal_name,
474 					entryPtr->unix_uid, entryPtr->comment);
475 				free(entryPtr->principal_name);
476 				free(entryPtr->comment);
477 				entryTmpPtr = entryPtr->next;
478 				free(entryPtr);
479 				entryPtr = entryTmpPtr;
480 			}
481 		}
482 
483 		/* any errors to display */
484 		if (errDetails) {
485 			fprintf(stderr,
486 				gettext("\nError searching gsscred table "
487 					"[%s]."),
488 				errDetails);
489 			free(errDetails);
490 			errDetails = NULL;
491 			status = 0;
492 		}
493 	}	/* for */
494 
495 cleanup:
496 	if (mechSet != NULL)
497 		gss_release_oid_set(&minor, &mechSet);
498 
499 	return (status);
500 }  /* listUsers */
501 
502 /*
503  * Performs additional handling while searching for users
504  * stored in the flat file table.
505  */
506 int
507 file_listUsers(const gss_OID mechOid, const char *unixUid,
508 		char **errDetails)
509 {
510 	gss_buffer_desc mechBufDesc = GSS_C_EMPTY_BUFFER,
511 		mechHexBufDesc = GSS_C_EMPTY_BUFFER;
512 	char mechBuf[128], mechHexBuf[256];
513 
514 	if (mechOid != NULL) {
515 		/* must make the name header whic contains mech oid */
516 		mechBufDesc.value = (void *) mechBuf;
517 		mechBufDesc.length = sizeof (mechBuf);
518 		mechHexBufDesc.value = (void*) mechHexBuf;
519 		mechHexBufDesc.length = sizeof (mechHexBuf);
520 
521 		if ((!gsscred_MakeNameHeader(mechOid, &mechBufDesc)) ||
522 			(!gsscred_AsHex(&mechBufDesc, &mechHexBufDesc))) {
523 			(*errDetails) = strdup(
524 					gettext("\nInternal error. "
525 					" Conversion to hex failed."));
526 			return (0);
527 		}
528 
529 		return (file_getGssCredEntry(&mechHexBufDesc,
530 				unixUid, errDetails));
531 	}
532 
533 	return (file_getGssCredEntry(NULL, unixUid, errDetails));
534 }  /* file_listUsers */
535 
536 
537 /*
538  *  Handles the deletion of users.
539  */
540 static int removeUsers(const char *name, const char *nameOidStr,
541 		const char *uidStr, const char *mechOidStr)
542 {
543 	char hexMech[256],
544 		hexName[(MAX_STR_LEN *2) + 1],
545 		*errDetails = NULL;
546 	gss_OID anOid = NULL, userMechOid = NULL;
547 	gss_OID_set mechSet = NULL;
548 	gss_buffer_desc inBufDesc = GSS_C_EMPTY_BUFFER,
549 		outBufDesc = GSS_C_EMPTY_BUFFER,
550 		searchName = GSS_C_EMPTY_BUFFER;
551 	int status = 0, numOfMechs, i;
552 	OM_uint32 minor;
553 
554 
555 	/* user deletion can only be performed by super user */
556 	if (getuid()) {
557 
558 		fprintf(stderr,
559 			gettext("\nUser deletion requires"
560 				" root privileges."));
561 		return (0);
562 	}
563 
564 	/* do we need to convert the mechanism oid? */
565 	if (mechOidStr != NULL) {
566 		if (__gss_mech_to_oid(mechOidStr, &userMechOid) !=
567 		GSS_S_COMPLETE) {
568 			fprintf(stderr,
569 				gettext("\nInvalid mechanism specified [%s]."),
570 				mechOidStr);
571 			return (0);
572 		}
573 
574 		inBufDesc.length = userMechOid->length;
575 		inBufDesc.value = userMechOid->elements;
576 		outBufDesc.length = sizeof (hexMech);
577 		outBufDesc.value = hexMech;
578 
579 		if (!gsscred_AsHex(&inBufDesc, &outBufDesc)) {
580 			fprintf(stderr,
581 				gettext("\nInternal error."
582 					"  Conversion to hex failed."));
583 			status = 0;
584 			goto cleanup;
585 		}
586 
587 	}	 /* mechOidStr != NULL */
588 
589 	/* are we deleting the entire table or an entire mech ? */
590 	if (name == NULL && uidStr == NULL) {
591 
592 		if (tableSource == GSSCRED_FLAT_FILE)
593 			status = file_removeUsers(userMechOid,
594 					NULL, &errDetails);
595 		else
596 			status = 0;
597 
598 		/* display any errors */
599 		if (errDetails) {
600 			fprintf(stderr,
601 				gettext("\nError deleting gsscred entry "
602 					"[%s]."),
603 				errDetails);
604 			free(errDetails);
605 			errDetails = NULL;
606 		}
607 		goto cleanup;
608 	}
609 
610 	/* are we deleting by uid or uid and mech? */
611 	if (name == NULL && uidStr != NULL) {
612 
613 		if (tableSource == GSSCRED_FLAT_FILE)
614 			status = file_removeUsers(userMechOid, uidStr,
615 						&errDetails);
616 		else
617 			status = 0;
618 
619 		/* check for any errors */
620 		if (errDetails) {
621 			fprintf(stderr,
622 				gettext("\nError deleting gsscred entry "
623 					"[%s]."),
624 				errDetails);
625 			free(errDetails);
626 			errDetails = NULL;
627 		}
628 		goto cleanup;
629 	}
630 
631 	/*
632 	 * We are deleting by name;
633 	 * how many mechs must we check?
634 	 */
635 	if (mechOidStr == NULL) {
636 
637 		if (gss_indicate_mechs(&minor, &mechSet) != GSS_S_COMPLETE) {
638 			fprintf(stderr,
639 				gettext("\nInternal error.  "
640 					"GSS-API call failed."));
641 			status = 0;
642 			goto cleanup;
643 		}
644 		numOfMechs = mechSet->count;
645 	}
646 	else
647 		numOfMechs = 1;
648 
649 	/* now look through all the mechs, deleting */
650 	for (i = 0; i < numOfMechs; i++) {
651 
652 		if (mechOidStr == NULL) {
653 			anOid = &mechSet->elements[i];
654 			inBufDesc.length = anOid->length;
655 			inBufDesc.value = anOid->elements;
656 			outBufDesc.length = sizeof (hexMech);
657 			outBufDesc.value = hexMech;
658 			if (!gsscred_AsHex(&inBufDesc, &outBufDesc))
659 				continue;
660 		} else
661 			anOid = userMechOid;
662 
663 		/* create a gss name */
664 		if (!gsscred_MakeName(anOid, name, nameOidStr, &outBufDesc))
665 			continue;
666 
667 		/* now convert it to hex, and delete it */
668 		searchName.value = hexName;
669 		searchName.length = sizeof (hexName);
670 		status = gsscred_AsHex(&outBufDesc, &searchName);
671 		free(outBufDesc.value);
672 
673 		if (!status)
674 			continue;
675 
676 		if (tableSource == GSSCRED_FLAT_FILE)
677 			status = file_deleteGssCredEntry(&searchName,
678 					uidStr, &errDetails);
679 		else
680 			status = 0;
681 
682 		/* check for any errors */
683 		if (errDetails) {
684 			fprintf(stderr,
685 				gettext("\nError deleting gsscred entry"
686 					" [%s]."),
687 				errDetails);
688 			free(errDetails);
689 			errDetails = NULL;
690 		}
691 	}	 /* for */
692 
693 cleanup:
694 	if (mechSet != NULL)
695 		gss_release_oid_set(&minor, &mechSet);
696 
697 	return (status);
698 }  /* removeUsers */
699 
700 
701 /*
702  * Performs additional handling while deleting users
703  * stored in the flat file table.
704  */
705 int file_removeUsers(const gss_OID mechOid, const char *unixUid,
706 		char **errDetails)
707 {
708 	gss_buffer_desc mechBufDesc = GSS_C_EMPTY_BUFFER,
709 		mechHexBufDesc = GSS_C_EMPTY_BUFFER;
710 	char mechBuf[128], mechHexBuf[256];
711 
712 	if (mechOid != NULL) {
713 		/*
714 		 * need to create the buffer header which contains
715 		 * the mechanism oid.
716 		 */
717 		mechBufDesc.value = (void*) mechBuf;
718 		mechBufDesc.length = sizeof (mechBuf);
719 		mechHexBufDesc.value = (void *) mechHexBuf;
720 		mechHexBufDesc.length = sizeof (mechHexBuf);
721 
722 		if ((!gsscred_MakeNameHeader(mechOid, &mechBufDesc)) ||
723 		    (!gsscred_AsHex(&mechBufDesc, &mechHexBufDesc))) {
724 			(*errDetails) = strdup(
725 				gettext("\nInternal error."
726 					"  Conversion to hex failed."));
727 			return (0);
728 		}
729 
730 		return (file_deleteGssCredEntry(&mechHexBufDesc, unixUid,
731 						errDetails));
732 	}
733 
734 	return (file_deleteGssCredEntry(NULL, unixUid, errDetails));
735 }  /* file_removeUsers */
736 
737 
738 /*
739  * Prints the usage string, and terminates.
740  */
741 static void usage(void)
742 {
743 
744 	fprintf(stderr,
745 		gettext("\nUsage:\t %s [-n user [-o oid] [-u uid]]"
746 			" [-c comment] -m mech -a"
747 			"\n\t %s [-n user [-o oid]] [-u uid] [-m mech] -r"
748 			"\n\t %s [-n user [-o oid]] [-u uid] [-m mech] -l\n"),
749 		PROG_NAME, PROG_NAME, PROG_NAME);
750 	exit(1);
751 }  /* usage */
752