Files
cdesktop/cde/lib/pam/pam_modules/dce/dce_password.c
2018-04-28 12:30:20 -06:00

498 lines
11 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 libraries and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/* $XConsortium: dce_password.c /main/5 1996/05/09 04:26:43 drk $ */
/*
* Copyright (c) 1995, by Sun Microsystems, Inc.
* All rights reserved.
*/
#ident "@(#)dce_password.c 1.22 96/02/14 SMI"
#include <security/pam_appl.h>
#include <security/pam_modules.h>
#include <syslog.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <pwd.h>
#include <dce/acct.h>
#include <dce/uuid.h>
#include <dce/binding.h>
#include <dce/sec_login.h>
#include <dce/dce_error.h>
#include "utils.h"
#include "pam_impl.h"
#ifdef XFN_MAPPING
#include "xfn_mapping.h"
#endif
static int
dce_changepw(
char *account_name,
char *old_pass,
char *new_pass);
static char *
get_passwd(
pam_handle_t *pamh,
char *prompt);
/*
* XXX: This module is NOT finished!
*
*/
int
pam_sm_chauthtok(
pam_handle_t *pamh,
int flags,
int argc,
const char **argv)
{
char *user;
int err, result = PAM_AUTH_ERR;
char *newpass = NULL, *vnewpass = NULL;
char *oldpass = NULL;
int try_first_pass = 0;
int use_first_pass = 0;
char *firstpass = NULL;
#ifdef XFN_MAPPING
int try_mapped_pass = 0;
int use_mapped_pass = 0;
uid_t saved_uid;
#endif
int i;
int debug = 0;
uid_t pw_uid;
dce_module_data_t *dsd = NULL;
error_status_t status;
if (debug)
syslog(LOG_DEBUG, "DCE Authentication\n");
for (i = 0; i < argc; i++) {
if (strcmp(argv[i], "debug") == 0)
debug = 1;
else if (strcmp(argv[i], "try_first_pass") == 0)
try_first_pass = 1;
else if (strcmp(argv[i], "use_first_pass") == 0)
use_first_pass = 1;
#ifdef XFN_MAPPING
else if (strcmp(argv[i], "try_mapped_pass") == 0)
try_mapped_pass = 1;
else if (strcmp(argv[i], "use_mapped_pass") == 0)
use_mapped_pass = 1;
#endif
else
syslog(LOG_DEBUG, "illegal scheme option %s", argv[i]);
}
if (flags & PAM_PRELIM_CHECK) {
/* try and bind to registry master of local cell */
sec_rgy_handle_t rh = sec_rgy_default_handle;
sec_rgy_site_open_update(NULL, &rh, &status);
if (status == error_status_ok) {
sec_rgy_site_close(rh, &status);
return (PAM_SUCCESS);
}
return (PAM_AUTHTOK_ERR);
}
/* make sure PAM framework is telling us to update passwords */
if (!(flags & PAM_UPDATE_AUTHTOK)) {
syslog(LOG_ERR, "dce pam_sm_chauthtok: bad flags: %d", flags);
return (PAM_SYSTEM_ERR);
}
if (flags & PAM_CHANGE_EXPIRED_AUTHTOK) {
if (pam_get_data(pamh, DCE_DATA, (void **)&dsd)
== PAM_SUCCESS) {
if (!dsd->passwd_expired)
return (PAM_IGNORE);
}
}
if ((err = pam_get_item(pamh, PAM_USER, (void **) &user)) < 0)
return (err);
/* Don't bother to handle root in DCE */
if (strcmp(user, "root") == 0)
return (PAM_IGNORE);
#ifdef XFN_MAPPING
if (use_mapped_pass || try_mapped_pass) {
int got_mapped = 0, updated_mapped = 0;
char dcepass[MAP_PASSLEN+1];
if ((err = pam_get_item(pamh, PAM_AUTHTOK,
(void **) &newpass)) < 0)
return (err);
if ((err = pam_get_item(pamh, PAM_OLDAUTHTOK,
(void **) &oldpass)) < 0)
return (err);
if (!get_pw_uid(user, &pw_uid)) {
return (PAM_AUTHTOK_ERR);
}
/* XXX: need to seteuid */
saved_uid = geteuid();
if (saved_uid != pw_uid && getuid() == 0 &&
seteuid(pw_uid) < 0) {
syslog(LOG_ERR,
"xfn_get_mapped_passwd: seteuid: %m");
return (PAM_AUTHTOK_ERR);
}
got_mapped = xfn_get_mapped_password(0, user,
DCE_XFN_PASS_ATTR, oldpass, dcepass, sizeof (dcepass));
if (got_mapped) {
updated_mapped = xfn_update_mapped_password(
0, user, DCE_XFN_PASS_ATTR, newpass,
dcepass);
memset(dcepass, 0, sizeof (dcepass));
} else {
/* probably should prompt for DCE password */
/* and attempt to update it */
}
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 (use_mapped_pass) {
if (updated_mapped)
return (PAM_SUCCESS);
else
return (PAM_AUTHTOK_ERR);
}
return (PAM_AUTHTOK_ERR);
}
#endif /* XFN_MAPPING */
if (try_first_pass || use_first_pass) {
if ((err = pam_get_item(pamh, PAM_AUTHTOK,
(void **) &newpass)) < 0)
return (err);
if ((err = pam_get_item(pamh, PAM_OLDAUTHTOK,
(void **) &oldpass)) < 0)
return (err);
result = dce_changepw(user, oldpass, newpass);
if (result == PAM_SUCCESS) {
/* might need to update PAM_OLDAUTHTOK and PAM_NEWAUTHTOK */
goto out;
}
/* assume we need to prompt for old DCE password? */
oldpass = get_passwd(pamh, PAM_MSG(pamh, 20,
"Old DCE password: "));
if (oldpass == NULL || oldpass[0] == '\0') {
/* Need a password to proceed */
result = PAM_AUTHTOK_ERR;
goto out;
}
result = dce_changepw(user, oldpass, newpass);
if (result == PAM_SUCCESS) {
/* might need to update PAM_OLDAUTHTOK and PAM_NEWAUTHTOK */
}
goto out;
}
/* prompt for both old and new passwords */
if (firstpass == NULL && !(try_first_pass||try_mapped_pass))
oldpass = get_passwd(pamh, PAM_MSG(pamh, 20,
"Old DCE password: "));
else
oldpass = get_passwd(pamh, PAM_MSG(pamh, 20,
"Old DCE password: "));
if (oldpass == NULL || oldpass[0] == '\0') {
/* Need a password to proceed */
result = PAM_AUTHTOK_ERR;
goto out;
}
if (firstpass == NULL && !(try_first_pass||try_mapped_pass))
newpass = get_passwd(pamh, PAM_MSG(pamh, 21,
"New DCE password: "));
else
newpass = get_passwd(pamh, PAM_MSG(pamh, 21,
"New DCE password: "));
if (newpass == NULL || newpass[0] == '\0') {
/* Need a password to proceed */
result = PAM_AUTHTOK_ERR;
goto out;
}
if (firstpass == NULL && !(try_first_pass||try_mapped_pass))
vnewpass = get_passwd(pamh,
PAM_MSG(pamh, 22,
"Re-enter new DCE password: "));
else
vnewpass = get_passwd(pamh,
PAM_MSG(pamh, 22,
"Re-enter new DCE password: "));
if (vnewpass == NULL || vnewpass[0] == '\0') {
/* Need a password to proceed */
result = PAM_AUTHTOK_ERR;
goto out;
}
if (strcmp(newpass, vnewpass)) {
result = PAM_AUTHTOK_ERR;
goto out;
}
result = dce_changepw(user, oldpass, newpass);
if (result == PAM_SUCCESS) {
/* might need to update PAM_OLDAUTHTOK and PAM_NEWAUTHTOK) */
}
out:
if (oldpass) {
memset(oldpass, 0, strlen(oldpass));
free(oldpass);
}
if (newpass) {
memset(newpass, 0, strlen(newpass));
free(newpass);
}
if (vnewpass) {
memset(vnewpass, 0, strlen(vnewpass));
free(vnewpass);
}
return (result);
}
static int
dce_changepw(
char *account_name,
char *old_pass,
char *new_pass)
{
error_status_t status;
sec_rgy_handle_t rgy_handle = sec_rgy_default_handle;
sec_rgy_login_name_t name_key;
sec_rgy_cursor_t account_cursor;
sec_rgy_login_name_t name_result;
sec_rgy_sid_t id_sid;
sec_rgy_unix_sid_t unix_sid;
sec_rgy_acct_key_t key_parts;
sec_rgy_acct_user_t user_part;
sec_rgy_acct_admin_t admin_part;
sec_passwd_rec_t new_key, caller_key;
sec_passwd_version_t new_key_version;
sec_login_handle_t login_context = sec_login_default_handle;
sec_passwd_rec_t passwd_rec;
boolean32 reset_pass = 0, login_valid = 0;
sec_login_auth_src_t auth_src;
char *env;
static char *krb5 = "KRB5CCNAME";
char *krb5_value = NULL;
/* stash away the value of KRB5CCNAME, if there is one set */
env = getenv(krb5);
if (env) {
krb5_value = malloc(strlen(krb5)+1+strlen(env)+1);
if (krb5_value)
sprintf(krb5_value, "%s=%s", krb5, env);
else {
status = sec_login_s_no_current_context;
goto out;
}
}
/* first we a get a login context. A future version should */
/* check and see if there is a current context with the */
/* same name as the user we are setting */
if (!sec_login_setup_identity((unsigned_char_p_t) account_name,
sec_login_no_flags, &login_context, &status)) {
goto out;
}
/* have to strdup password because the call clears it */
passwd_rec.key.tagged_union.plain =
(idl_char *) strdup(old_pass);
if (passwd_rec.key.tagged_union.plain == NULL) {
status = sec_login_s_no_current_context;
goto out;
}
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_validate_identity(login_context,
&passwd_rec, &reset_pass, &auth_src, &status);
free(passwd_rec.key.tagged_union.plain); /* already memset */
if (!login_valid)
goto out;
sec_login_set_context(login_context, &status);
if (status != error_status_ok)
goto out;
/* we now have a context with the same account as the one */
/* we are trying to change the password on. Lets talk to the */
/* registry... */
sec_rgy_site_open(NULL, &rgy_handle, &status);
if (status != error_status_ok) {
goto out;
}
caller_key.key.key_type = sec_passwd_plain;
caller_key.pepper = NULL;
caller_key.version_number = sec_passwd_c_version_none;
caller_key.key.tagged_union.plain = (idl_char *) old_pass;
sec_rgy_cursor_reset(&account_cursor);
strcpy((char *)name_key.pname, account_name);
name_key.gname[0] = '\0';
name_key.oname[0] = '\0';
/* need to do this in order to get the org and group for the */
/* account. All we want is name_result... */
sec_rgy_acct_lookup(
rgy_handle,
&name_key,
&account_cursor,
&name_result,
&id_sid,
&unix_sid,
&key_parts,
&user_part,
&admin_part,
&status);
if (status != error_status_ok)
goto out;
caller_key.key.key_type = sec_passwd_plain;
caller_key.pepper = NULL;
caller_key.version_number = sec_passwd_c_version_none;
caller_key.key.tagged_union.plain = (idl_char *) old_pass;
new_key.key.key_type = sec_passwd_plain;
new_key.pepper = NULL;
new_key.version_number = sec_passwd_c_version_none;
new_key.key.tagged_union.plain = (idl_char *) new_pass;
/* rock and roll. lets try to update the password... */
sec_rgy_acct_passwd(
rgy_handle,
&name_result,
&caller_key,
&new_key,
sec_passwd_des,
&new_key_version,
&status);
out:
/* restore the old KRB5CCNAME value */
if (krb5_value) {
putenv(krb5_value);
}
if (rgy_handle != sec_rgy_default_handle) {
error_status_t st; /* don't trash status */
sec_rgy_site_close(rgy_handle, &st);
}
if (login_context != sec_login_default_handle) {
error_status_t st; /* don't trash status */
sec_login_purge_context(&login_context, &st);
}
if (status == error_status_ok) {
return (PAM_SUCCESS);
} else {
return (PAM_AUTHTOK_ERR);
}
}
static char *
get_passwd(
pam_handle_t *pamh,
char *prompt)
{
int err;
char *p;
err = __pam_get_authtok(pamh, PAM_PROMPT, 0, 256, prompt, &p);
if (err != PAM_SUCCESS) {
return (NULL);
}
return (p);
}