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