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