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