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 * $Id: pamtest.c 685 2013-07-11 16:33:34Z 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 */ 49 extern const char *pam_item_name[PAM_NUM_ITEMS]; 50 extern int openpam_debug; 51 52 static pam_handle_t *pamh; 53 static struct pam_conv pamc; 54 55 static int silent; 56 static int verbose; 57 58 static void pt_verbose(const char *, ...) 59 OPENPAM_FORMAT ((__printf__, 1, 2)); 60 static 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 */ 66 static void 67 pt_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 */ 82 static void 83 pt_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 */ 98 static int 99 pt_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 */ 113 static int 114 pt_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 */ 128 static int 129 pt_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 */ 143 static int 144 pt_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 */ 158 static int 159 pt_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 */ 173 static int 174 pt_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 */ 188 static int 189 pt_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 */ 203 static int 204 pt_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 */ 234 static int 235 pt_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 */ 247 static int 248 pt_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 */ 269 static void 270 usage(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 */ 281 static void 282 opt_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 */ 299 static void 300 opt_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 */ 313 int 314 main(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 464 end: 465 (void)pt_end(pame); 466 exit(pame == PAM_SUCCESS ? 0 : 1); 467 } 468