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