cdesktopenv/cde/lib/pam/pam_modules/dce/dce_authenticate.c

457 lines
10 KiB
C

/*
* CDE - Common Desktop Environment
*
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with these librararies and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/* $XConsortium: dce_authenticate.c /main/5 1996/05/09 04:26:26 drk $ */
/*
* Copyright (c) 1995, by Sun Microsystems, Inc.
* All rights reserved.
*/
#ident "@(#)dce_authenticate.c 1.34 96/02/14 SMI"
#include <dce/nbase.h>
#include <dce/sec_login.h>
#include <dce/dce_error.h>
#include <security/pam_appl.h>
#include <security/pam_modules.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <rpc/des_crypt.h>
#include <pwd.h>
#include <syslog.h>
#include <libintl.h>
#include "pam_impl.h"
#include "utils.h"
#ifdef XFN_MAPPING
#include "xfn_mapping.h"
#endif /* XFN_MAPPING */
#define SLEEPTIME 4
/* maxmimum DCE_PASSWD_LENGTH. We need to pick something for
* __pam_get_authtok to use.
*/
#define DCE_PASSWD_LENGTH 256
static int
attempt_dce_login(
void *pamh,
dce_module_data_t *dsd,
error_status_t *st,
char *user,
char *dce_pass
);
void
dce_cleanup(
pam_handle_t *pamh,
void *data,
int pam_status
);
/*
* pam_sm_authenticate - Authenticate user
*/
int
pam_sm_authenticate(
pam_handle_t *pamh,
int flags,
int argc,
const char **argv)
{
char *user;
int err, result = PAM_AUTH_ERR;
char messages[1][PAM_MAX_MSG_SIZE];
error_status_t st;
char *defpass;
int num_msg = 0;
int debug = 0;
int warn = 1;
int passwd_flag = 0;
int try_first_pass = 0;
int use_first_pass = 0;
int ignore = 0;
int invalid_user = 0;
#ifdef XFN_MAPPING
int try_mapped_pass = 0;
int use_mapped_pass = 0;
#endif
int i;
char *firstpass = NULL, *password = NULL;
uid_t pw_uid;
dce_module_data_t *dsd = NULL;
for (i = 0; i < argc; i++) {
if (strcmp(argv[i], "debug") == 0)
debug = 1;
else if (strcmp(argv[i], "try_first_pass") == 0) {
if (!passwd_flag) {
try_first_pass = 1;
passwd_flag = 1;
}
} else if (strcmp(argv[i], "use_first_pass") == 0) {
if (!passwd_flag) {
use_first_pass = 1;
passwd_flag = 1;
}
#ifdef XFN_MAPPING
} else if (strcmp(argv[i], "try_mapped_pass") == 0) {
if (!passwd_flag) {
try_mapped_pass = 1;
passwd_flag = 1;
}
} else if (strcmp(argv[i], "use_mapped_pass") == 0) {
if (!passwd_flag) {
use_mapped_pass = 1;
passwd_flag = 1;
}
#endif
} else if (strcmp(argv[i], "nowarn") == 0) {
warn = 0;
} else {
syslog(LOG_ERR, "illegal module option %s", argv[i]);
}
}
if (flags & PAM_SILENT) warn = 0;
if (debug)
syslog(LOG_DEBUG, "DCE pam_sm_authenticate");
err = pam_get_user(pamh, &user, NULL);
if (err != PAM_SUCCESS)
return (err);
if (user == NULL || !user[0])
return (PAM_AUTH_ERR);
/* Don't bother to authenticate root in DCE */
if (strcmp(user, "root") == 0)
ignore = 1;
/* make sure a password entry exists for this user */
/* we also need the uid for XFN */
if (!get_pw_uid(user, &pw_uid)) {
invalid_user = 1;
}
if (pam_get_data(pamh, DCE_DATA, (void**)&dsd) != PAM_SUCCESS ||
dsd == NULL) {
dsd = calloc(1, sizeof (dce_module_data_t));
if (dsd == NULL) {
result = PAM_BUF_ERR;
goto out;
}
if ((err = pam_set_data(pamh, DCE_DATA, dsd, &dce_cleanup))
!= PAM_SUCCESS) {
free(dsd);
result = err;
goto out;
}
} else {
if (dsd->login_context != sec_login_default_handle) {
error_status_t st;
sec_login_purge_context(&dsd->login_context, &st);
}
}
dsd->login_context = sec_login_default_handle;
dsd->auth_status = PAM_AUTH_ERR;
dsd->debug = debug;
dsd->warn = warn;
dsd->reset_passwd = 0;
dsd->passwd_expired = 0;
dsd->auth_src = sec_login_auth_src_network;
/* see if a legitimate DCE user */
if (!sec_login_setup_identity((unsigned char *)user,
sec_login_no_flags, &dsd->login_context, &st)) {
if (debug) {
dce_error_string_t text;
syslog(LOG_DEBUG,
"PAM: DCE sec_login_setup_identity: %s",
get_dce_error_message(st, text));
}
if (st == sec_rgy_object_not_found) {
/* mask the unknown user case */
invalid_user = 1;
} else {
result = PAM_AUTH_ERR;
goto out;
}
}
err = pam_get_item(pamh, PAM_AUTHTOK, (void **) &firstpass);
if (err != PAM_SUCCESS && (use_first_pass || use_mapped_pass)) {
result = PAM_AUTH_ERR;
if (debug)
syslog(LOG_DEBUG, "PAM: DCE goto out!");
goto out;
}
if (firstpass != NULL && (ignore || invalid_user))
goto out;
#ifdef XFN_MAPPING
if (firstpass == NULL) {
if (use_mapped_pass) goto out;
} else if (try_mapped_pass || use_mapped_pass) {
char dcepass[MAP_PASSLEN+1];
uid_t saved_uid;
int got_mapped_pass;
saved_uid = geteuid();
if (saved_uid != pw_uid && getuid() == 0 &&
seteuid(pw_uid) < 0) {
syslog(LOG_ERR,
"xfn_get_mapped_passwd: seteuid: %m");
/* continue since we might be able to get mapping */
}
got_mapped_pass = xfn_get_mapped_password(
debug ? XFN_MAP_DEBUG : 0, user, DCE_XFN_PASS_ATTR,
firstpass, dcepass, sizeof (dcepass));
if (geteuid() != saved_uid && seteuid(saved_uid) < 0) {
syslog(LOG_ERR,
"xfn_get_mapped_passwd seteuid restore: %m");
/* XXX: what should we do here? */
}
if (got_mapped_pass) {
result =
attempt_dce_login(pamh, dsd, &st, user, dcepass);
memset(dcepass, 0, MAP_PASSLEN);
if (result == PAM_SUCCESS ||
st == sec_login_s_acct_invalid) goto out;
}
if (use_mapped_pass) goto out;
}
#endif
if (firstpass == NULL) {
if (use_first_pass) goto out;
} else if (use_first_pass || try_first_pass) {
result = attempt_dce_login(pamh, dsd, &st,
user, firstpass);
if (result == PAM_SUCCESS ||
st == sec_login_s_acct_invalid ||
use_first_pass) {
goto out;
}
}
/*
* Get the password from the user
*/
if (debug)
syslog(LOG_DEBUG, "DCE pam_sm_auth prompting for password");
if (firstpass == NULL &&
!(try_first_pass||try_mapped_pass||invalid_user))
(void) sprintf(messages[0], (const char *) PAM_MSG(pamh, 10,
"Password: "));
else
(void) sprintf(messages[0], (const char *) PAM_MSG(pamh, 11,
"DCE Password: "));
num_msg = 1;
err = __pam_get_authtok(pamh, PAM_PROMPT, PAM_AUTHTOK,
DCE_PASSWD_LENGTH,messages[0], &password);
if (debug)
syslog(LOG_DEBUG, "DCE __pam_get_authtok = %d", err);
if (err != PAM_SUCCESS) {
result = err;
goto out;
}
if (password == NULL) {
/* Need a password to proceed */
result = PAM_AUTH_ERR;
goto out;
}
if (firstpass == NULL) {
/* this is the first password, stash it away */
pam_set_item(pamh, PAM_AUTHTOK, password);
}
/* one last ditch attempt to login to DCE */
if (invalid_user || ignore)
goto out;
result = attempt_dce_login(pamh, dsd, &st, user, password);
#ifdef XFN_MAPPING
/* we had to prompt for DCE password, so attempt to */
/* update mapping iff we got a good DCE password */
if (try_mapped_pass && result == PAM_SUCCESS && firstpass) {
uid_t saved_uid;
saved_uid = geteuid();
if (saved_uid != pw_uid && seteuid(pw_uid) < 0) {
syslog(LOG_ERR, "xfn_get_mapped_passwd: seteuid: %m");
goto out;
}
xfn_update_mapped_password(debug ? XFN_MAP_DEBUG : 0,
user, DCE_XFN_PASS_ATTR, firstpass, password);
if (geteuid() != saved_uid && seteuid(saved_uid) < 0) {
syslog(LOG_ERR,
"xfn_get_mapped_passwd seteuid restore: %m");
/* XXX: what should we do here? */
}
}
#endif
out:
if (ignore) {
result = PAM_IGNORE;
}
if (password != NULL)
memset(password, 0, strlen(password));
if (invalid_user)
result = PAM_USER_UNKNOWN;
if (dsd)
dsd->auth_status = result;
return (result);
}
static int
attempt_dce_login(
void *pamh,
dce_module_data_t *dsd,
error_status_t *st,
char *user,
char *dce_pass)
{
sec_passwd_rec_t passwd_rec;
boolean32 login_valid = 0;
error_status_t set_st;
/* have to strdup password because the call clears it */
passwd_rec.key.tagged_union.plain = (idl_char *) strdup(dce_pass);
if (passwd_rec.key.tagged_union.plain == NULL) {
return (PAM_BUF_ERR);
}
passwd_rec.key.key_type = sec_passwd_plain;
passwd_rec.pepper = NULL;
passwd_rec.version_number = sec_passwd_c_version_none;
login_valid = sec_login_valid_and_cert_ident(dsd->login_context,
&passwd_rec, &dsd->reset_passwd, &dsd->auth_src, st);
if (dsd->debug) {
dce_error_string_t text;
syslog(LOG_DEBUG, "sec_login_valid_and_cert_ident: %s",
get_dce_error_message(*st, text));
}
if (*st == sec_login_s_acct_invalid && dsd->warn) {
char messages[1][PAM_MAX_MSG_SIZE];
sprintf(messages[0],
PAM_MSG(pamh, 12, "Error: Your DCE Account has expired.\n"));
__pam_display_msg(pamh, PAM_ERROR_MSG, 1, messages, NULL);
}
/* the call to sec_login_valid_and_cert_ident already zeros */
/* out the password, so we just free it */
free(passwd_rec.key.tagged_union.plain);
return (login_valid ? PAM_SUCCESS : PAM_AUTH_ERR);
}
void
dce_cleanup(
pam_handle_t *pamh,
void *data,
int pam_status)
{
error_status_t st;
dce_module_data_t *dsd = (dce_module_data_t *) data;
int status;
if (dsd->debug) {
syslog(LOG_DEBUG, "DCE dce_cleanup pam_sm_auth_status(%d)",
dsd->auth_status);
}
if (!dsd->login_context != sec_login_default_handle) {
free(dsd);
return;
}
/* if pam_end as PAM_SUCCESS, clean up based on value in */
/* auth_status, otherwise just purge the context */
if (pam_status == PAM_SUCCESS) {
pam_sec_login_free_context(dsd->auth_status,
&dsd->login_context, &st);
} else {
sec_login_purge_context(&dsd->login_context, &st);
if (dsd->debug) {
dce_error_string_t text;
syslog(LOG_DEBUG, "sec_login_purge_context: %s",
get_dce_error_message(st, text));
}
}
free(dsd);
}