159301Skris/*- 259301Skris * Copyright 2000 James Bloom 359301Skris * All rights reserved. 494564Sdes * Based upon code Copyright 1998 Juniper Networks, Inc. 5115470Sdes * Copyright (c) 2001-2003 Networks Associates Technology, Inc. 687398Sdes * All rights reserved. 759301Skris * 887398Sdes * Portions of this software were developed for the FreeBSD Project by 987398Sdes * ThinkSec AS and NAI Labs, the Security Research Division of Network 1087398Sdes * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 1187398Sdes * ("CBOSS"), as part of the DARPA CHATS research program. 1287398Sdes * 1359301Skris * Redistribution and use in source and binary forms, with or without 1459301Skris * modification, are permitted provided that the following conditions 1559301Skris * are met: 1659301Skris * 1. Redistributions of source code must retain the above copyright 1759301Skris * notice, this list of conditions and the following disclaimer. 1859301Skris * 2. Redistributions in binary form must reproduce the above copyright 1959301Skris * notice, this list of conditions and the following disclaimer in the 2059301Skris * documentation and/or other materials provided with the distribution. 2187398Sdes * 3. The name of the author may not be used to endorse or promote 2287398Sdes * products derived from this software without specific prior written 2387398Sdes * permission. 2459301Skris * 2559301Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2659301Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2759301Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2859301Skris * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2959301Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3059301Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3159301Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3259301Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3359301Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3459301Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3559301Skris * SUCH DAMAGE. 3659301Skris */ 3759301Skris 3884218Sdillon#include <sys/cdefs.h> 3984218Sdillon__FBSDID("$FreeBSD$"); 4084218Sdillon 4179476Smarkm#include <sys/types.h> 4279476Smarkm#include <opie.h> 4379476Smarkm#include <pwd.h> 4459301Skris#include <stdio.h> 45100917Sdes#include <stdlib.h> 4659301Skris#include <string.h> 4779476Smarkm#include <unistd.h> 4859301Skris 4959301Skris#define PAM_SM_AUTH 5087398Sdes 5190229Sdes#include <security/pam_appl.h> 5259301Skris#include <security/pam_modules.h> 5390229Sdes#include <security/pam_mod_misc.h> 5459301Skris 55115465Sdes#define PAM_OPT_NO_FAKE_PROMPTS "no_fake_prompts" 5679476Smarkm 5759301SkrisPAM_EXTERN int 5894564Sdespam_sm_authenticate(pam_handle_t *pamh, int flags __unused, 59115465Sdes int argc __unused, const char *argv[] __unused) 6059301Skris{ 6159301Skris struct opie opie; 6279476Smarkm struct passwd *pwd; 6389567Sache int retval, i; 6489760Smarkm const char *(promptstr[]) = { "%s\nPassword: ", "%s\nPassword [echo on]: "}; 65270125Sache char challenge[OPIE_CHALLENGE_MAX + 1]; 66162320Sdes char principal[OPIE_PRINCIPAL_MAX]; 67162320Sdes const char *user; 68100917Sdes char *response; 69100917Sdes int style; 7059301Skris 7179476Smarkm user = NULL; 72115465Sdes if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF)) { 7389592Sache if ((pwd = getpwnam(getlogin())) == NULL) 7494564Sdes return (PAM_AUTH_ERR); 7579476Smarkm user = pwd->pw_name; 7679476Smarkm } 7779476Smarkm else { 78162320Sdes retval = pam_get_user(pamh, &user, NULL); 7979476Smarkm if (retval != PAM_SUCCESS) 8094564Sdes return (retval); 8179476Smarkm } 8279476Smarkm 8379476Smarkm PAM_LOG("Got user: %s", user); 8479476Smarkm 8559301Skris /* 86162320Sdes * Watch out: libopie feels entitled to truncate the user name 87162320Sdes * passed to it if it's longer than OPIE_PRINCIPAL_MAX, which is 88162320Sdes * not uncommon in Windows environments. 89162320Sdes */ 90162320Sdes if (strlen(user) >= sizeof(principal)) 91162320Sdes return (PAM_AUTH_ERR); 92162320Sdes strlcpy(principal, user, sizeof(principal)); 93162320Sdes 94162320Sdes /* 9559301Skris * Don't call the OPIE atexit() handler when our program exits, 9659301Skris * since the module has been unloaded and we will SEGV. 9759301Skris */ 9859301Skris opiedisableaeh(); 9959301Skris 10089618Sdes /* 10189618Sdes * If the no_fake_prompts option was given, and the user 10289618Sdes * doesn't have an OPIE key, just fail rather than present the 10389618Sdes * user with a bogus OPIE challenge. 10489618Sdes */ 105162320Sdes if (opiechallenge(&opie, principal, challenge) != 0 && 106115465Sdes openpam_get_option(pamh, PAM_OPT_NO_FAKE_PROMPTS)) 10794564Sdes return (PAM_AUTH_ERR); 10894564Sdes 10989618Sdes /* 11089618Sdes * It doesn't make sense to use a password that has already been 11189618Sdes * typed in, since we haven't presented the challenge to the user 11289618Sdes * yet, so clear the stored password. 11389618Sdes */ 11489618Sdes pam_set_item(pamh, PAM_AUTHTOK, NULL); 11594564Sdes 116100917Sdes style = PAM_PROMPT_ECHO_OFF; 11779476Smarkm for (i = 0; i < 2; i++) { 118100917Sdes retval = pam_prompt(pamh, style, &response, 119100917Sdes promptstr[i], challenge); 12079476Smarkm if (retval != PAM_SUCCESS) { 12159301Skris opieunlock(); 12294564Sdes return (retval); 12359301Skris } 12479476Smarkm 12579476Smarkm PAM_LOG("Completed challenge %d: %s", i, response); 12679476Smarkm 12779476Smarkm if (response[0] != '\0') 12879476Smarkm break; 12979476Smarkm 13079476Smarkm /* Second time round, echo the password */ 131100917Sdes style = PAM_PROMPT_ECHO_ON; 13259301Skris } 13379476Smarkm 134100917Sdes pam_set_item(pamh, PAM_AUTHTOK, response); 13579476Smarkm 13659301Skris /* 13759301Skris * Opieverify is supposed to return -1 only if an error occurs. 13859301Skris * But it returns -1 even if the response string isn't in the form 13959301Skris * it expects. Thus we can't log an error and can only check for 14059301Skris * success or lack thereof. 14159301Skris */ 142100917Sdes retval = opieverify(&opie, response); 143100917Sdes free(response); 144100917Sdes return (retval == 0 ? PAM_SUCCESS : PAM_AUTH_ERR); 14559301Skris} 14659301Skris 14759301SkrisPAM_EXTERN int 14894564Sdespam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused, 14994564Sdes int argc __unused, const char *argv[] __unused) 15059301Skris{ 15181473Smarkm 15294564Sdes return (PAM_SUCCESS); 15359301Skris} 15459301Skris 15559301SkrisPAM_MODULE_ENTRY("pam_opie"); 156