pamtest.c revision 1.2
1/*	$NetBSD: pamtest.c,v 1.2 2011/12/25 22:27:55 christos Exp $	*/
2
3/*-
4 * Copyright (c) 2011 Dag-Erling Sm��rgrav
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer
12 *    in this position and unchanged.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * Id: pamtest.c 472 2011-11-03 09:46:52Z des
30 */
31
32#ifdef HAVE_CONFIG_H
33# include "config.h"
34#endif
35
36#include <err.h>
37#include <pwd.h>
38#include <stdarg.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <unistd.h>
43
44#include <security/pam_appl.h>
45#include <security/openpam.h>	/* for openpam_ttyconv() */
46
47/* OpenPAM internals */
48extern const char *pam_item_name[PAM_NUM_ITEMS];
49extern int openpam_debug;
50
51static pam_handle_t *pamh;
52static struct pam_conv pamc;
53
54static int silent;
55static int verbose;
56
57static void pt_verbose(const char *, ...)
58	OPENPAM_FORMAT ((__printf__, 1, 2));
59static void pt_error(int, const char *, ...)
60	OPENPAM_FORMAT ((__printf__, 2, 3));
61
62/*
63 * Print an information message if -v was specified at least once
64 */
65static void
66pt_verbose(const char *fmt, ...)
67{
68	va_list ap;
69
70	if (verbose) {
71		va_start(ap, fmt);
72		vfprintf(stderr, fmt, ap);
73		va_end(ap);
74		fprintf(stderr, "\n");
75	}
76}
77
78/*
79 * Print an error message
80 */
81static void
82pt_error(int e, const char *fmt, ...)
83{
84	va_list ap;
85
86	if (e == PAM_SUCCESS && !verbose)
87		return;
88	va_start(ap, fmt);
89	vfprintf(stderr, fmt, ap);
90	va_end(ap);
91	fprintf(stderr, ": %s\n", pam_strerror(NULL, e));
92}
93
94/*
95 * Wrapper for pam_start(3)
96 */
97static int
98pt_start(const char *service, const char *user)
99{
100	int pame;
101
102	pamc.conv = &openpam_ttyconv;
103	pt_verbose("pam_start(%s, %s)", service, user);
104	if ((pame = pam_start(service, user, &pamc, &pamh)) != PAM_SUCCESS)
105		pt_error(pame, "pam_start(%s)", service);
106	return (pame);
107}
108
109/*
110 * Wrapper for pam_authenticate(3)
111 */
112static int
113pt_authenticate(int flags)
114{
115	int pame;
116
117	flags |= silent;
118	if ((pame = pam_authenticate(pamh, flags)) != PAM_SUCCESS)
119		pt_error(pame, "pam_authenticate()");
120	return (pame);
121}
122
123/*
124 * Wrapper for pam_acct_mgmt(3)
125 */
126static int
127pt_acct_mgmt(int flags)
128{
129	int pame;
130
131	flags |= silent;
132	if ((pame = pam_acct_mgmt(pamh, flags)) != PAM_SUCCESS)
133		pt_error(pame, "pam_acct_mgmt()");
134	return (pame);
135}
136
137/*
138 * Wrapper for pam_chauthtok(3)
139 */
140static int
141pt_chauthtok(int flags)
142{
143	int pame;
144
145	flags |= silent;
146	if ((pame = pam_chauthtok(pamh, flags)) != PAM_SUCCESS)
147		pt_error(pame, "pam_chauthtok()");
148	return (pame);
149}
150
151/*
152 * Wrapper for pam_setcred(3)
153 */
154static int
155pt_setcred(int flags)
156{
157	int pame;
158
159	flags |= silent;
160	if ((pame = pam_setcred(pamh, flags)) != PAM_SUCCESS)
161		pt_error(pame, "pam_setcred()");
162	return (pame);
163}
164
165/*
166 * Wrapper for pam_open_session(3)
167 */
168static int
169pt_open_session(int flags)
170{
171	int pame;
172
173	flags |= silent;
174	if ((pame = pam_open_session(pamh, flags)) != PAM_SUCCESS)
175		pt_error(pame, "pam_open_session()");
176	return (pame);
177}
178
179/*
180 * Wrapper for pam_close_session(3)
181 */
182static int
183pt_close_session(int flags)
184{
185	int pame;
186
187	flags |= silent;
188	if ((pame = pam_close_session(pamh, flags)) != PAM_SUCCESS)
189		pt_error(pame, "pam_close_session()");
190	return (pame);
191}
192
193/*
194 * Wrapper for pam_set_item(3)
195 */
196static int
197pt_set_item(int item, const char *p)
198{
199	int pame;
200
201	switch (item) {
202	case PAM_SERVICE:
203	case PAM_USER:
204	case PAM_AUTHTOK:
205	case PAM_OLDAUTHTOK:
206	case PAM_TTY:
207	case PAM_RHOST:
208	case PAM_RUSER:
209	case PAM_USER_PROMPT:
210	case PAM_AUTHTOK_PROMPT:
211	case PAM_OLDAUTHTOK_PROMPT:
212	case PAM_HOST:
213		pt_verbose("setting %s to %s", pam_item_name[item], p);
214		break;
215	default:
216		pt_verbose("setting %s", pam_item_name[item]);
217		break;
218	}
219	if ((pame = pam_set_item(pamh, item, p)) != PAM_SUCCESS)
220		pt_error(pame, "pam_set_item(%s)", pam_item_name[item]);
221	return (pame);
222}
223
224/*
225 * Wrapper for pam_end(3)
226 */
227static int
228pt_end(int pame)
229{
230
231	if (pamh != NULL && (pame = pam_end(pamh, pame)) != PAM_SUCCESS)
232		/* can't happen */
233		pt_error(pame, "pam_end()");
234	return (pame);
235}
236
237/*
238 * Retrieve and list the PAM environment variables
239 */
240static int
241pt_listenv(void)
242{
243	char **pam_envlist, **pam_env;
244
245	if ((pam_envlist = pam_getenvlist(pamh)) == NULL ||
246	    *pam_envlist == NULL) {
247		pt_verbose("no environment variables.");
248	} else {
249		pt_verbose("environment variables:");
250		for (pam_env = pam_envlist; *pam_env != NULL; ++pam_env) {
251			printf(" %s\n", *pam_env);
252			free(*pam_env);
253		}
254	}
255	free(pam_envlist);
256	return (PAM_SUCCESS);
257}
258
259/*
260 * Print usage string and exit
261 */
262static void
263usage(void)
264{
265
266	fprintf(stderr, "usage: pamtest [-dksv] %s\n",
267	    "[-H rhost] [-h host] [-t tty] [-U ruser] [-u user] service");
268	exit(1);
269}
270
271/*
272 * Handle an option that takes a string argument and can be used only once
273 */
274static void
275opt_str_once(int opt, const char **p, const char *arg)
276{
277
278	if (*p != NULL) {
279		fprintf(stderr, "The -%c option can only be used once\n", opt);
280		usage();
281	}
282	*p = arg;
283}
284
285/*
286 * Entry point
287 */
288int
289main(int argc, char *argv[])
290{
291	char hostname[1024];
292	const char *rhost = NULL;
293	const char *host = NULL;
294	const char *ruser = NULL;
295	const char *user = NULL;
296	const char *service = NULL;
297	const char *tty = NULL;
298	int keepatit = 0;
299	int pame;
300	int opt;
301
302	while ((opt = getopt(argc, argv, "dH:h:kst:U:u:v")) != -1)
303		switch (opt) {
304		case 'd':
305			openpam_debug++;
306			break;
307		case 'H':
308			opt_str_once(opt, &rhost, optarg);
309			break;
310		case 'h':
311			opt_str_once(opt, &host, optarg);
312			break;
313		case 'k':
314			keepatit = 1;
315			break;
316		case 's':
317			silent = PAM_SILENT;
318			break;
319		case 't':
320			opt_str_once(opt, &tty, optarg);
321			break;
322		case 'U':
323			opt_str_once(opt, &ruser, optarg);
324			break;
325		case 'u':
326			opt_str_once(opt, &user, optarg);
327			break;
328		case 'v':
329			verbose++;
330			break;
331		default:
332			usage();
333		}
334
335	argc -= optind;
336	argv += optind;
337
338	if (argc < 1)
339		usage();
340
341	service = *argv;
342	--argc;
343	++argv;
344
345	/* defaults */
346	if (rhost == NULL) {
347		if (gethostname(hostname, sizeof(hostname)) == -1)
348			err(1, "gethostname()");
349		rhost = hostname;
350	}
351	if (tty == NULL)
352		tty = ttyname(STDERR_FILENO);
353	if (user == NULL)
354		user = getlogin();
355	if (ruser == NULL)
356		ruser = user;
357
358	/* initialize PAM */
359	if ((pame = pt_start(service, user)) != PAM_SUCCESS)
360		goto end;
361
362	/*
363	 * pam_start(3) sets this to the machine's hostname, but we allow
364	 * the user to override it.
365	 */
366	if (host != NULL)
367		if ((pame = pt_set_item(PAM_HOST, host)) != PAM_SUCCESS)
368		    goto end;
369
370	/*
371	 * The remote host / user / tty are usually set by the
372	 * application.
373	 */
374	if ((pame = pt_set_item(PAM_RHOST, rhost)) != PAM_SUCCESS ||
375	    (pame = pt_set_item(PAM_RUSER, ruser)) != PAM_SUCCESS ||
376	    (pame = pt_set_item(PAM_TTY, tty)) != PAM_SUCCESS)
377		goto end;
378
379	while (argc > 0) {
380		if (strcmp(*argv, "listenv") == 0 ||
381		    strcmp(*argv, "env") == 0) {
382			pame = pt_listenv();
383		} else if (strcmp(*argv, "authenticate") == 0 ||
384		    strcmp(*argv, "auth") == 0) {
385			pame = pt_authenticate(0);
386		} else if (strcmp(*argv, "acct_mgmt") == 0 ||
387		    strcmp(*argv, "account") == 0) {
388			pame = pt_acct_mgmt(0);
389		} else if (strcmp(*argv, "chauthtok") == 0 ||
390		    strcmp(*argv, "change") == 0) {
391			pame = pt_chauthtok(PAM_CHANGE_EXPIRED_AUTHTOK);
392		} else if (strcmp(*argv, "forcechauthtok") == 0 ||
393		    strcmp(*argv, "forcechange") == 0) {
394			pame = pt_chauthtok(0);
395		} else if (strcmp(*argv, "setcred") == 0 ||
396		    strcmp(*argv, "establish_cred") == 0) {
397			pame = pt_setcred(PAM_ESTABLISH_CRED);
398		} else if (strcmp(*argv, "open_session") == 0 ||
399		    strcmp(*argv, "open") == 0) {
400			pame = pt_open_session(0);
401		} else if (strcmp(*argv, "close_session") == 0 ||
402		    strcmp(*argv, "close") == 0) {
403			pame = pt_close_session(0);
404		} else if (strcmp(*argv, "unsetcred") == 0 ||
405		    strcmp(*argv, "delete_cred") == 0) {
406			pame = pt_setcred(PAM_DELETE_CRED);
407		} else {
408			warnx("unknown primitive: %s", *argv);
409			pame = PAM_SYSTEM_ERR;
410		}
411		if (pame != PAM_SUCCESS && !keepatit) {
412			warnx("test aborted");
413			break;
414		}
415		--argc;
416		++argv;
417	}
418
419end:
420	(void)pt_end(pame);
421	exit(pame == PAM_SUCCESS ? 0 : 1);
422}
423