1228690Sdes/*-
2228690Sdes * Copyright (c) 2011 Dag-Erling Sm��rgrav
3228690Sdes * All rights reserved.
4228690Sdes *
5228690Sdes * Redistribution and use in source and binary forms, with or without
6228690Sdes * modification, are permitted provided that the following conditions
7228690Sdes * are met:
8228690Sdes * 1. Redistributions of source code must retain the above copyright
9263421Sdes *    notice, this list of conditions and the following disclaimer.
10228690Sdes * 2. Redistributions in binary form must reproduce the above copyright
11228690Sdes *    notice, this list of conditions and the following disclaimer in the
12228690Sdes *    documentation and/or other materials provided with the distribution.
13247568Sdes * 3. The name of the author may not be used to endorse or promote
14247568Sdes *    products derived from this software without specific prior written
15247568Sdes *    permission.
16228690Sdes *
17228690Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18228690Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19228690Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20228690Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21228690Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22228690Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23228690Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24228690Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25228690Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26228690Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27228690Sdes * SUCH DAMAGE.
28228690Sdes *
29263421Sdes * $Id: pamtest.c 685 2013-07-11 16:33:34Z des $
30228690Sdes */
31228690Sdes
32228690Sdes#ifdef HAVE_CONFIG_H
33228690Sdes# include "config.h"
34228690Sdes#endif
35228690Sdes
36228690Sdes#include <err.h>
37263421Sdes#include <limits.h>
38228690Sdes#include <pwd.h>
39228690Sdes#include <stdarg.h>
40228690Sdes#include <stdio.h>
41228690Sdes#include <stdlib.h>
42228690Sdes#include <string.h>
43228690Sdes#include <unistd.h>
44228690Sdes
45228690Sdes#include <security/pam_appl.h>
46228690Sdes#include <security/openpam.h>	/* for openpam_ttyconv() */
47228690Sdes
48228690Sdes/* OpenPAM internals */
49228690Sdesextern const char *pam_item_name[PAM_NUM_ITEMS];
50228690Sdesextern int openpam_debug;
51228690Sdes
52228690Sdesstatic pam_handle_t *pamh;
53228690Sdesstatic struct pam_conv pamc;
54228690Sdes
55228690Sdesstatic int silent;
56228690Sdesstatic int verbose;
57228690Sdes
58228690Sdesstatic void pt_verbose(const char *, ...)
59228690Sdes	OPENPAM_FORMAT ((__printf__, 1, 2));
60228690Sdesstatic void pt_error(int, const char *, ...)
61228690Sdes	OPENPAM_FORMAT ((__printf__, 2, 3));
62228690Sdes
63228690Sdes/*
64228690Sdes * Print an information message if -v was specified at least once
65228690Sdes */
66228690Sdesstatic void
67228690Sdespt_verbose(const char *fmt, ...)
68228690Sdes{
69228690Sdes	va_list ap;
70228690Sdes
71228690Sdes	if (verbose) {
72228690Sdes		va_start(ap, fmt);
73228690Sdes		vfprintf(stderr, fmt, ap);
74228690Sdes		va_end(ap);
75228690Sdes		fprintf(stderr, "\n");
76228690Sdes	}
77228690Sdes}
78228690Sdes
79228690Sdes/*
80228690Sdes * Print an error message
81228690Sdes */
82228690Sdesstatic void
83228690Sdespt_error(int e, const char *fmt, ...)
84228690Sdes{
85228690Sdes	va_list ap;
86228690Sdes
87228690Sdes	if (e == PAM_SUCCESS && !verbose)
88228690Sdes		return;
89228690Sdes	va_start(ap, fmt);
90228690Sdes	vfprintf(stderr, fmt, ap);
91228690Sdes	va_end(ap);
92228690Sdes	fprintf(stderr, ": %s\n", pam_strerror(NULL, e));
93228690Sdes}
94228690Sdes
95228690Sdes/*
96228690Sdes * Wrapper for pam_start(3)
97228690Sdes */
98228690Sdesstatic int
99228690Sdespt_start(const char *service, const char *user)
100228690Sdes{
101228690Sdes	int pame;
102228690Sdes
103228690Sdes	pamc.conv = &openpam_ttyconv;
104228690Sdes	pt_verbose("pam_start(%s, %s)", service, user);
105228690Sdes	if ((pame = pam_start(service, user, &pamc, &pamh)) != PAM_SUCCESS)
106228690Sdes		pt_error(pame, "pam_start(%s)", service);
107228690Sdes	return (pame);
108228690Sdes}
109228690Sdes
110228690Sdes/*
111228690Sdes * Wrapper for pam_authenticate(3)
112228690Sdes */
113228690Sdesstatic int
114228690Sdespt_authenticate(int flags)
115228690Sdes{
116228690Sdes	int pame;
117228690Sdes
118228690Sdes	flags |= silent;
119263421Sdes	pt_verbose("pam_authenticate()");
120228690Sdes	if ((pame = pam_authenticate(pamh, flags)) != PAM_SUCCESS)
121228690Sdes		pt_error(pame, "pam_authenticate()");
122228690Sdes	return (pame);
123228690Sdes}
124228690Sdes
125228690Sdes/*
126228690Sdes * Wrapper for pam_acct_mgmt(3)
127228690Sdes */
128228690Sdesstatic int
129228690Sdespt_acct_mgmt(int flags)
130228690Sdes{
131228690Sdes	int pame;
132228690Sdes
133228690Sdes	flags |= silent;
134263421Sdes	pt_verbose("pam_acct_mgmt()");
135228690Sdes	if ((pame = pam_acct_mgmt(pamh, flags)) != PAM_SUCCESS)
136228690Sdes		pt_error(pame, "pam_acct_mgmt()");
137228690Sdes	return (pame);
138228690Sdes}
139228690Sdes
140228690Sdes/*
141228690Sdes * Wrapper for pam_chauthtok(3)
142228690Sdes */
143228690Sdesstatic int
144228690Sdespt_chauthtok(int flags)
145228690Sdes{
146228690Sdes	int pame;
147228690Sdes
148228690Sdes	flags |= silent;
149263421Sdes	pt_verbose("pam_chauthtok()");
150228690Sdes	if ((pame = pam_chauthtok(pamh, flags)) != PAM_SUCCESS)
151228690Sdes		pt_error(pame, "pam_chauthtok()");
152228690Sdes	return (pame);
153228690Sdes}
154228690Sdes
155228690Sdes/*
156228690Sdes * Wrapper for pam_setcred(3)
157228690Sdes */
158228690Sdesstatic int
159228690Sdespt_setcred(int flags)
160228690Sdes{
161228690Sdes	int pame;
162228690Sdes
163228690Sdes	flags |= silent;
164263421Sdes	pt_verbose("pam_setcred()");
165228690Sdes	if ((pame = pam_setcred(pamh, flags)) != PAM_SUCCESS)
166228690Sdes		pt_error(pame, "pam_setcred()");
167228690Sdes	return (pame);
168228690Sdes}
169228690Sdes
170228690Sdes/*
171228690Sdes * Wrapper for pam_open_session(3)
172228690Sdes */
173228690Sdesstatic int
174228690Sdespt_open_session(int flags)
175228690Sdes{
176228690Sdes	int pame;
177228690Sdes
178228690Sdes	flags |= silent;
179263421Sdes	pt_verbose("pam_open_session()");
180228690Sdes	if ((pame = pam_open_session(pamh, flags)) != PAM_SUCCESS)
181228690Sdes		pt_error(pame, "pam_open_session()");
182228690Sdes	return (pame);
183228690Sdes}
184228690Sdes
185228690Sdes/*
186228690Sdes * Wrapper for pam_close_session(3)
187228690Sdes */
188228690Sdesstatic int
189228690Sdespt_close_session(int flags)
190228690Sdes{
191228690Sdes	int pame;
192228690Sdes
193228690Sdes	flags |= silent;
194263421Sdes	pt_verbose("pam_close_session()");
195228690Sdes	if ((pame = pam_close_session(pamh, flags)) != PAM_SUCCESS)
196228690Sdes		pt_error(pame, "pam_close_session()");
197228690Sdes	return (pame);
198228690Sdes}
199228690Sdes
200228690Sdes/*
201228690Sdes * Wrapper for pam_set_item(3)
202228690Sdes */
203228690Sdesstatic int
204228690Sdespt_set_item(int item, const char *p)
205228690Sdes{
206228690Sdes	int pame;
207228690Sdes
208228690Sdes	switch (item) {
209228690Sdes	case PAM_SERVICE:
210228690Sdes	case PAM_USER:
211228690Sdes	case PAM_AUTHTOK:
212228690Sdes	case PAM_OLDAUTHTOK:
213228690Sdes	case PAM_TTY:
214228690Sdes	case PAM_RHOST:
215228690Sdes	case PAM_RUSER:
216228690Sdes	case PAM_USER_PROMPT:
217228690Sdes	case PAM_AUTHTOK_PROMPT:
218228690Sdes	case PAM_OLDAUTHTOK_PROMPT:
219228690Sdes	case PAM_HOST:
220228690Sdes		pt_verbose("setting %s to %s", pam_item_name[item], p);
221228690Sdes		break;
222228690Sdes	default:
223228690Sdes		pt_verbose("setting %s", pam_item_name[item]);
224228690Sdes		break;
225228690Sdes	}
226228690Sdes	if ((pame = pam_set_item(pamh, item, p)) != PAM_SUCCESS)
227228690Sdes		pt_error(pame, "pam_set_item(%s)", pam_item_name[item]);
228228690Sdes	return (pame);
229228690Sdes}
230228690Sdes
231228690Sdes/*
232228690Sdes * Wrapper for pam_end(3)
233228690Sdes */
234228690Sdesstatic int
235228690Sdespt_end(int pame)
236228690Sdes{
237228690Sdes
238228690Sdes	if (pamh != NULL && (pame = pam_end(pamh, pame)) != PAM_SUCCESS)
239228690Sdes		/* can't happen */
240228690Sdes		pt_error(pame, "pam_end()");
241228690Sdes	return (pame);
242228690Sdes}
243228690Sdes
244228690Sdes/*
245228690Sdes * Retrieve and list the PAM environment variables
246228690Sdes */
247228690Sdesstatic int
248228690Sdespt_listenv(void)
249228690Sdes{
250228690Sdes	char **pam_envlist, **pam_env;
251228690Sdes
252228690Sdes	if ((pam_envlist = pam_getenvlist(pamh)) == NULL ||
253228690Sdes	    *pam_envlist == NULL) {
254228690Sdes		pt_verbose("no environment variables.");
255228690Sdes	} else {
256228690Sdes		pt_verbose("environment variables:");
257228690Sdes		for (pam_env = pam_envlist; *pam_env != NULL; ++pam_env) {
258228690Sdes			printf(" %s\n", *pam_env);
259228690Sdes			free(*pam_env);
260228690Sdes		}
261228690Sdes	}
262228690Sdes	free(pam_envlist);
263228690Sdes	return (PAM_SUCCESS);
264228690Sdes}
265228690Sdes
266228690Sdes/*
267228690Sdes * Print usage string and exit
268228690Sdes */
269228690Sdesstatic void
270228690Sdesusage(void)
271228690Sdes{
272228690Sdes
273247568Sdes	fprintf(stderr, "usage: pamtest %s service command ...\n",
274247568Sdes	    "[-dkMPsv] [-H rhost] [-h host] [-t tty] [-U ruser] [-u user]");
275228690Sdes	exit(1);
276228690Sdes}
277228690Sdes
278228690Sdes/*
279263421Sdes * Handle an option that takes an int argument and can be used only once
280263421Sdes */
281263421Sdesstatic void
282263421Sdesopt_num_once(int opt, long *num, const char *arg)
283263421Sdes{
284263421Sdes	char *end;
285263421Sdes	long l;
286263421Sdes
287263421Sdes	l = strtol(arg, &end, 0);
288263421Sdes	if (end == optarg || *end != '\0') {
289263421Sdes		fprintf(stderr,
290263421Sdes		    "The -%c option expects a numeric argument\n", opt);
291263421Sdes		usage();
292263421Sdes	}
293263421Sdes	*num = l;
294263421Sdes}
295263421Sdes
296263421Sdes/*
297228690Sdes * Handle an option that takes a string argument and can be used only once
298228690Sdes */
299228690Sdesstatic void
300228690Sdesopt_str_once(int opt, const char **p, const char *arg)
301228690Sdes{
302228690Sdes
303228690Sdes	if (*p != NULL) {
304228690Sdes		fprintf(stderr, "The -%c option can only be used once\n", opt);
305228690Sdes		usage();
306228690Sdes	}
307228690Sdes	*p = arg;
308228690Sdes}
309228690Sdes
310228690Sdes/*
311228690Sdes * Entry point
312228690Sdes */
313228690Sdesint
314228690Sdesmain(int argc, char *argv[])
315228690Sdes{
316228690Sdes	char hostname[1024];
317228690Sdes	const char *rhost = NULL;
318228690Sdes	const char *host = NULL;
319228690Sdes	const char *ruser = NULL;
320228690Sdes	const char *user = NULL;
321228690Sdes	const char *service = NULL;
322228690Sdes	const char *tty = NULL;
323263421Sdes	long timeout = 0;
324228690Sdes	int keepatit = 0;
325228690Sdes	int pame;
326228690Sdes	int opt;
327228690Sdes
328263421Sdes	while ((opt = getopt(argc, argv, "dH:h:kMPsT:t:U:u:v")) != -1)
329228690Sdes		switch (opt) {
330228690Sdes		case 'd':
331228690Sdes			openpam_debug++;
332228690Sdes			break;
333228690Sdes		case 'H':
334228690Sdes			opt_str_once(opt, &rhost, optarg);
335228690Sdes			break;
336228690Sdes		case 'h':
337228690Sdes			opt_str_once(opt, &host, optarg);
338228690Sdes			break;
339228690Sdes		case 'k':
340228690Sdes			keepatit = 1;
341228690Sdes			break;
342247568Sdes		case 'M':
343247568Sdes			openpam_set_feature(OPENPAM_RESTRICT_MODULE_NAME, 0);
344247568Sdes			openpam_set_feature(OPENPAM_VERIFY_MODULE_FILE, 0);
345247568Sdes			break;
346247568Sdes		case 'P':
347247568Sdes			openpam_set_feature(OPENPAM_RESTRICT_SERVICE_NAME, 0);
348247568Sdes			openpam_set_feature(OPENPAM_VERIFY_POLICY_FILE, 0);
349247568Sdes			break;
350228690Sdes		case 's':
351228690Sdes			silent = PAM_SILENT;
352228690Sdes			break;
353263421Sdes		case 'T':
354263421Sdes			opt_num_once(opt, &timeout, optarg);
355263421Sdes			if (timeout < 0 || timeout > INT_MAX) {
356263421Sdes				fprintf(stderr,
357263421Sdes				    "Invalid conversation timeout\n");
358263421Sdes				usage();
359263421Sdes			}
360263421Sdes			openpam_ttyconv_timeout = (int)timeout;
361263421Sdes			break;
362228690Sdes		case 't':
363228690Sdes			opt_str_once(opt, &tty, optarg);
364228690Sdes			break;
365228690Sdes		case 'U':
366228690Sdes			opt_str_once(opt, &ruser, optarg);
367228690Sdes			break;
368228690Sdes		case 'u':
369228690Sdes			opt_str_once(opt, &user, optarg);
370228690Sdes			break;
371228690Sdes		case 'v':
372228690Sdes			verbose++;
373228690Sdes			break;
374228690Sdes		default:
375228690Sdes			usage();
376228690Sdes		}
377228690Sdes
378228690Sdes	argc -= optind;
379228690Sdes	argv += optind;
380228690Sdes
381228690Sdes	if (argc < 1)
382228690Sdes		usage();
383228690Sdes
384228690Sdes	service = *argv;
385228690Sdes	--argc;
386228690Sdes	++argv;
387228690Sdes
388228690Sdes	/* defaults */
389263421Sdes	if (service == NULL)
390263421Sdes		service = "pamtest";
391228690Sdes	if (rhost == NULL) {
392228690Sdes		if (gethostname(hostname, sizeof(hostname)) == -1)
393228690Sdes			err(1, "gethostname()");
394228690Sdes		rhost = hostname;
395228690Sdes	}
396228690Sdes	if (tty == NULL)
397228690Sdes		tty = ttyname(STDERR_FILENO);
398228690Sdes	if (user == NULL)
399228690Sdes		user = getlogin();
400228690Sdes	if (ruser == NULL)
401228690Sdes		ruser = user;
402228690Sdes
403228690Sdes	/* initialize PAM */
404228690Sdes	if ((pame = pt_start(service, user)) != PAM_SUCCESS)
405228690Sdes		goto end;
406228690Sdes
407228690Sdes	/*
408228690Sdes	 * pam_start(3) sets this to the machine's hostname, but we allow
409228690Sdes	 * the user to override it.
410228690Sdes	 */
411228690Sdes	if (host != NULL)
412228690Sdes		if ((pame = pt_set_item(PAM_HOST, host)) != PAM_SUCCESS)
413228690Sdes		    goto end;
414228690Sdes
415228690Sdes	/*
416228690Sdes	 * The remote host / user / tty are usually set by the
417228690Sdes	 * application.
418228690Sdes	 */
419228690Sdes	if ((pame = pt_set_item(PAM_RHOST, rhost)) != PAM_SUCCESS ||
420228690Sdes	    (pame = pt_set_item(PAM_RUSER, ruser)) != PAM_SUCCESS ||
421228690Sdes	    (pame = pt_set_item(PAM_TTY, tty)) != PAM_SUCCESS)
422228690Sdes		goto end;
423228690Sdes
424228690Sdes	while (argc > 0) {
425228690Sdes		if (strcmp(*argv, "listenv") == 0 ||
426228690Sdes		    strcmp(*argv, "env") == 0) {
427228690Sdes			pame = pt_listenv();
428228690Sdes		} else if (strcmp(*argv, "authenticate") == 0 ||
429228690Sdes		    strcmp(*argv, "auth") == 0) {
430228690Sdes			pame = pt_authenticate(0);
431228690Sdes		} else if (strcmp(*argv, "acct_mgmt") == 0 ||
432228690Sdes		    strcmp(*argv, "account") == 0) {
433228690Sdes			pame = pt_acct_mgmt(0);
434228690Sdes		} else if (strcmp(*argv, "chauthtok") == 0 ||
435228690Sdes		    strcmp(*argv, "change") == 0) {
436228690Sdes			pame = pt_chauthtok(PAM_CHANGE_EXPIRED_AUTHTOK);
437228690Sdes		} else if (strcmp(*argv, "forcechauthtok") == 0 ||
438228690Sdes		    strcmp(*argv, "forcechange") == 0) {
439228690Sdes			pame = pt_chauthtok(0);
440228690Sdes		} else if (strcmp(*argv, "setcred") == 0 ||
441228690Sdes		    strcmp(*argv, "establish_cred") == 0) {
442228690Sdes			pame = pt_setcred(PAM_ESTABLISH_CRED);
443228690Sdes		} else if (strcmp(*argv, "open_session") == 0 ||
444228690Sdes		    strcmp(*argv, "open") == 0) {
445228690Sdes			pame = pt_open_session(0);
446228690Sdes		} else if (strcmp(*argv, "close_session") == 0 ||
447228690Sdes		    strcmp(*argv, "close") == 0) {
448228690Sdes			pame = pt_close_session(0);
449228690Sdes		} else if (strcmp(*argv, "unsetcred") == 0 ||
450228690Sdes		    strcmp(*argv, "delete_cred") == 0) {
451228690Sdes			pame = pt_setcred(PAM_DELETE_CRED);
452228690Sdes		} else {
453228690Sdes			warnx("unknown primitive: %s", *argv);
454228690Sdes			pame = PAM_SYSTEM_ERR;
455228690Sdes		}
456228690Sdes		if (pame != PAM_SUCCESS && !keepatit) {
457228690Sdes			warnx("test aborted");
458228690Sdes			break;
459228690Sdes		}
460228690Sdes		--argc;
461228690Sdes		++argv;
462228690Sdes	}
463228690Sdes
464228690Sdesend:
465228690Sdes	(void)pt_end(pame);
466228690Sdes	exit(pame == PAM_SUCCESS ? 0 : 1);
467228690Sdes}
468