997 lines
24 KiB
C
997 lines
24 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: unix_utils.c /main/5 1996/05/09 04:36:55 drk $ */
|
|
/*
|
|
* Copyright (c) 1992-1995, by Sun Microsystems, Inc.
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#ident "@(#)unix_utils.c 1.53 95/09/11 SMI"
|
|
|
|
#include "unix_headers.h"
|
|
|
|
static char *spskip();
|
|
static int special_case();
|
|
static int illegal_input();
|
|
static int copy_passwd_structs(struct passwd **, struct passwd *,
|
|
struct spwd **, struct spwd *);
|
|
|
|
void free_passwd_structs(struct passwd *, struct spwd *);
|
|
|
|
/* ******************************************************************** */
|
|
/* */
|
|
/* Utilities Functions */
|
|
/* */
|
|
/* ******************************************************************** */
|
|
|
|
#ifdef PAM_SECURE_RPC
|
|
|
|
/*
|
|
* Establish the Secure RPC secret key for the given uid using
|
|
* the given password to decrypt the secret key, and store it with
|
|
* the key service.
|
|
*
|
|
* If called with a nonzero 'reestablish' parameter, the key
|
|
* is obtained from the name service, decrypted, and stored
|
|
* even if the keyserver already has a key stored for the uid.
|
|
* If the 'reestablish' parameter is zero, the function will not
|
|
* try to reset the key. It will return immediately with
|
|
* ESTKEY_ALREADY.
|
|
*
|
|
* Returns one of the following codes:
|
|
* ESTKEY_ALREADY - reestablish flag was zero, and key was already set.
|
|
* ESTKEY_SUCCESS - successfully obtained, decrypted, and set the key
|
|
* ESTKEY_NOCREDENTIALS - the user has no credentials.
|
|
* ESTKEY_BADPASSWD - the password supplied didn't decrypt the key
|
|
* ESTKEY_CANTSETKEY - decrypted the key, but couldn't store key with
|
|
* the key service.
|
|
*
|
|
* If netnamebuf is a non-NULL pointer, the netname will be returned in
|
|
* netnamebuf, provided that the return status is not
|
|
* ESTKEY_NOCREDENTIALS or ESTKEY_ALREADY. If non-NULL, the
|
|
* netnamebuf pointer must point to a buffer of length at least
|
|
* MAXNETNAMELEN+1 characters.
|
|
*/
|
|
|
|
int
|
|
establish_key(uid, password, reestablish, netnamebuf)
|
|
uid_t uid;
|
|
char *password;
|
|
int reestablish;
|
|
char *netnamebuf;
|
|
{
|
|
char netname[MAXNETNAMELEN+1];
|
|
struct key_netstarg netst;
|
|
uid_t orig_uid;
|
|
|
|
orig_uid = geteuid();
|
|
if (seteuid(uid) == -1)
|
|
/* can't set uid */
|
|
return (ESTKEY_NOCREDENTIALS);
|
|
|
|
|
|
if (!reestablish && key_secretkey_is_set()) {
|
|
/* key is already established and we are not to reestablish */
|
|
(void) seteuid(orig_uid);
|
|
return (ESTKEY_ALREADY);
|
|
}
|
|
|
|
|
|
if (!getnetname(netname)) {
|
|
/* can't construct netname */
|
|
(void) seteuid(orig_uid);
|
|
return (ESTKEY_NOCREDENTIALS);
|
|
}
|
|
|
|
if (!getsecretkey(netname, (char *) &(netst.st_priv_key), password)) {
|
|
/* no secret key */
|
|
(void) seteuid(orig_uid);
|
|
return (ESTKEY_NOCREDENTIALS);
|
|
}
|
|
|
|
if (netnamebuf) {
|
|
/* return copy of netname in caller's buffer */
|
|
(void) strcpy(netnamebuf, netname);
|
|
}
|
|
|
|
if (netst.st_priv_key[0] == 0) {
|
|
/* password does not decrypt secret key */
|
|
(void) seteuid(orig_uid);
|
|
return (ESTKEY_BADPASSWD);
|
|
}
|
|
|
|
|
|
/* secret key successfully decrypted at this point */
|
|
|
|
/* store with key service */
|
|
|
|
if ((netst.st_netname = strdup(netname)) == NULL) {
|
|
(void) seteuid(orig_uid);
|
|
return (PAM_BUF_ERR);
|
|
}
|
|
(void) memset(netst.st_pub_key, 0, HEXKEYBYTES);
|
|
|
|
if (key_setnet(&netst) < 0) {
|
|
free(netst.st_netname);
|
|
(void) seteuid(orig_uid);
|
|
return (ESTKEY_CANTSETKEY);
|
|
}
|
|
|
|
free(netst.st_netname);
|
|
(void) seteuid(orig_uid);
|
|
return (ESTKEY_SUCCESS);
|
|
}
|
|
|
|
#endif /* PAM_SECURE_RPC */
|
|
|
|
/* ******************************************************************** */
|
|
/* */
|
|
/* Utilities Functions */
|
|
/* */
|
|
/* ******************************************************************** */
|
|
|
|
/*
|
|
* ck_perm():
|
|
* Check the permission of the user specified by "usrname".
|
|
*
|
|
* It returns PAM_PERM_DENIED if (1) the user has a NULL pasword or
|
|
* shadow password file entry, or (2) the caller is not root and
|
|
* its uid is not equivalent to the uid specified by the user's
|
|
* password file entry.
|
|
*/
|
|
|
|
int
|
|
ck_perm(pamh, repository, domain, pwd, shpwd, privileged, passwd_res, uid,
|
|
debug, nowarn)
|
|
pam_handle_t *pamh;
|
|
int repository;
|
|
char *domain;
|
|
struct passwd **pwd;
|
|
struct spwd **shpwd;
|
|
int *privileged;
|
|
void **passwd_res;
|
|
uid_t uid;
|
|
int debug;
|
|
int nowarn;
|
|
{
|
|
FILE *pwfp, *spfp;
|
|
char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
|
|
struct passwd local_pwd, *local_pwdp;
|
|
struct spwd local_shpwd, *local_shpwdp;
|
|
char pwdbuf[1024], shpwdbuf[1024];
|
|
char *prognamep;
|
|
char *usrname;
|
|
int retcode = 0;
|
|
#ifdef PAM_NISPLUS
|
|
char buf[NIS_MAXNAMELEN+1];
|
|
nis_name local_principal;
|
|
nis_name pwd_domain;
|
|
#endif
|
|
|
|
if (debug)
|
|
syslog(LOG_DEBUG,
|
|
"ck_perm() called: repository=%s",
|
|
repository_to_string(repository));
|
|
|
|
if ((retcode = pam_get_item(pamh, PAM_SERVICE, (void **)&prognamep))
|
|
!= PAM_SUCCESS ||
|
|
(retcode = pam_get_item(pamh, PAM_USER, (void **)&usrname))
|
|
!= PAM_SUCCESS) {
|
|
*pwd = NULL; *shpwd = NULL;
|
|
return (retcode);
|
|
}
|
|
|
|
if (repository == PAM_REP_FILES) {
|
|
if (((pwfp = fopen(PASSWD, "r")) == NULL) ||
|
|
((spfp = fopen(SHADOW, "r")) == NULL)) {
|
|
*pwd = NULL; *shpwd = NULL;
|
|
syslog(LOG_ERR,
|
|
"ck_perm: can not open passwd/shadow file");
|
|
return (PAM_PERM_DENIED);
|
|
}
|
|
while ((local_pwdp = fgetpwent_r(pwfp, &local_pwd, pwdbuf,
|
|
sizeof (pwdbuf))) != NULL)
|
|
if (strcmp(local_pwd.pw_name, usrname) == 0)
|
|
break;
|
|
while ((local_shpwdp = fgetspent_r(spfp, &local_shpwd,
|
|
shpwdbuf, sizeof (shpwdbuf))) != NULL)
|
|
if (strcmp(local_shpwd.sp_namp, usrname) == 0)
|
|
break;
|
|
(void) fclose(pwfp);
|
|
(void) fclose(spfp);
|
|
if (local_pwdp == NULL || local_shpwdp == NULL) {
|
|
*pwd = NULL; *shpwd = NULL;
|
|
return (PAM_USER_UNKNOWN);
|
|
}
|
|
|
|
if (uid != 0 && uid != local_pwd.pw_uid) {
|
|
/*
|
|
* Change passwd for another person:
|
|
* Even if you are nis+ admin, you can't do anything
|
|
* locally. Don't bother to continue.
|
|
*/
|
|
if (!nowarn) {
|
|
sprintf(messages[0], PAM_MSG(pamh, 140,
|
|
"%s%s: Permission denied"), prognamep,
|
|
UNIX_MSG);
|
|
sprintf(messages[1], PAM_MSG(pamh, 141,
|
|
"%s%s: Can't change local passwd file\n"),
|
|
prognamep, UNIX_MSG);
|
|
(void) __pam_display_msg(
|
|
pamh, PAM_ERROR_MSG, 2,
|
|
messages, NULL);
|
|
}
|
|
*pwd = NULL; *shpwd = NULL;
|
|
return (PAM_PERM_DENIED);
|
|
}
|
|
return (copy_passwd_structs(pwd, local_pwdp,
|
|
shpwd, local_shpwdp));
|
|
}
|
|
|
|
#ifdef PAM_NIS
|
|
if (repository == PAM_REP_NIS) {
|
|
/*
|
|
* Special case root: don't bother to get root from nis(yp).
|
|
*/
|
|
if (strcmp(usrname, "root") == 0) {
|
|
*pwd = NULL; *shpwd = NULL;
|
|
return (PAM_USER_UNKNOWN);
|
|
}
|
|
|
|
/* get pwd struct from yp */
|
|
|
|
local_pwdp = getpwnam_from(usrname, PAM_REP_NIS);
|
|
local_shpwdp = getspnam_from(usrname, PAM_REP_NIS);
|
|
|
|
if (local_pwdp == NULL || local_shpwdp == NULL)
|
|
return (PAM_USER_UNKNOWN);
|
|
|
|
if (uid != local_pwdp->pw_uid) {
|
|
if (!nowarn) {
|
|
sprintf(messages[0], PAM_MSG(pamh, 140,
|
|
"%s%s: Permission denied"), prognamep,
|
|
NIS_MSG);
|
|
(void) __pam_display_msg(pamh, PAM_ERROR_MSG,
|
|
1, messages, NULL);
|
|
}
|
|
*pwd = NULL; *shpwd = NULL;
|
|
return (PAM_PERM_DENIED);
|
|
}
|
|
return (copy_passwd_structs(pwd, local_pwdp,
|
|
shpwd, local_shpwdp));
|
|
}
|
|
#endif /* PAM_NIS */
|
|
|
|
#ifdef PAM_NISPLUS
|
|
if (repository == PAM_REP_NISPLUS) {
|
|
/*
|
|
* Special case root: don't bother to get root from nis+.
|
|
*/
|
|
if (strcmp(usrname, "root") == 0) {
|
|
*pwd = NULL; *shpwd = NULL;
|
|
return (PAM_USER_UNKNOWN);
|
|
}
|
|
|
|
if (debug)
|
|
syslog(LOG_DEBUG, "ck_perm(): NIS+ domain=%s", domain);
|
|
|
|
/*
|
|
* We need to use user id to
|
|
* make any nis+ request. But don't give up the super
|
|
* user power yet. It may be needed elsewhere.
|
|
*/
|
|
(void) setuid(0); /* keep real user id as root */
|
|
(void) seteuid(uid);
|
|
|
|
local_shpwdp = getspnam_from(usrname, PAM_REP_NISPLUS);
|
|
local_pwdp = getpwnam_from(usrname, PAM_REP_NISPLUS);
|
|
|
|
if (local_pwdp == NULL || local_shpwdp == NULL)
|
|
return (PAM_USER_UNKNOWN);
|
|
|
|
/*
|
|
* local_principal is internal, it is not meant to be free()ed
|
|
*/
|
|
local_principal = nis_local_principal();
|
|
|
|
if ((9 + strlen(usrname) + strlen(domain) + PASSTABLELEN) >
|
|
(size_t) NIS_MAXNAMELEN) {
|
|
sprintf(messages[0], PAM_MSG(pamh, 140,
|
|
"%s%s: Permission denied"), prognamep, NISPLUS_MSG);
|
|
(void) __pam_display_msg(pamh, PAM_ERROR_MSG,
|
|
1, messages, NULL);
|
|
*pwd = NULL; *shpwd = NULL;
|
|
return (PAM_PERM_DENIED);
|
|
}
|
|
|
|
sprintf(buf, "[name=%s],%s.%s", usrname, PASSTABLE, domain);
|
|
if (buf[strlen(buf) - 1] != '.')
|
|
(void) strcat(buf, ".");
|
|
|
|
/*
|
|
* We must use an authenticated handle to get the cred
|
|
* table information for the user we want to modify the
|
|
* cred info for. If we can't even read that info, we
|
|
* definitely wouldn't have modify permission. Well..
|
|
*/
|
|
*passwd_res = (void *) nis_list(buf,
|
|
USE_DGRAM+FOLLOW_LINKS+FOLLOW_PATH+MASTER_ONLY,
|
|
NULL, NULL);
|
|
if (((nis_result *)(*passwd_res))->status != NIS_SUCCESS) {
|
|
sprintf(messages[0], PAM_MSG(pamh, 140,
|
|
"%s%s: Permission denied"), prognamep, NISPLUS_MSG);
|
|
(void) __pam_display_msg(pamh, PAM_ERROR_MSG,
|
|
1, messages, NULL);
|
|
*pwd = NULL; *shpwd = NULL;
|
|
return (PAM_PERM_DENIED);
|
|
}
|
|
|
|
pwd_domain =
|
|
NIS_RES_OBJECT((nis_result *)(*passwd_res))->zo_domain;
|
|
if (strcmp(nis_leaf_of(pwd_domain), "org_dir") == 0) {
|
|
pwd_domain = nis_domain_of(
|
|
NIS_RES_OBJECT((nis_result *)(*passwd_res))->zo_domain);
|
|
|
|
}
|
|
*privileged = __nis_isadmin(local_principal, "passwd",
|
|
pwd_domain);
|
|
|
|
return (copy_passwd_structs(pwd, local_pwdp,
|
|
shpwd, local_shpwdp));
|
|
}
|
|
#endif /* PAM_NISPLUS */
|
|
|
|
if (!nowarn) {
|
|
sprintf(messages[0], PAM_MSG(pamh, 142,
|
|
"%s%s: System error: repository out of range"),
|
|
prognamep, UNIX_MSG);
|
|
(void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1,
|
|
messages, NULL);
|
|
}
|
|
*pwd = NULL; *shpwd = NULL;
|
|
return (PAM_PERM_DENIED);
|
|
}
|
|
|
|
/*
|
|
* attr_match():
|
|
*
|
|
* Check if the attribute name in string s1 is equivalent to
|
|
* that in string s2.
|
|
* s1 is either name, or name=value
|
|
* s2 is name=value
|
|
* if names match, return value of s2, else NULL
|
|
*/
|
|
|
|
char *
|
|
attr_match(s1, s2)
|
|
char *s1, *s2;
|
|
{
|
|
while (*s1 == *s2++)
|
|
if (*s1++ == '=')
|
|
return (s2);
|
|
if (*s1 == '\0' && *(s2-1) == '=')
|
|
return (s2);
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* attr_find():
|
|
*
|
|
* Check if the attribute name in string s1 is present in the
|
|
* attribute=value pairs array pointed by s2.
|
|
* s1 is name
|
|
* s2 is an array of name=value pairs
|
|
* if s1 match the name of any one of the name in the name=value pairs
|
|
* pointed by s2, then 1 is returned; else 0 is returned
|
|
*/
|
|
|
|
int
|
|
attr_find(s1, s2)
|
|
char *s1, *s2[];
|
|
{
|
|
int i;
|
|
char *sa, *sb;
|
|
|
|
i = 0;
|
|
while (s2[i] != NULL) {
|
|
sa = s1;
|
|
sb = s2[i];
|
|
while (*sa++ == *sb++) {
|
|
if ((*sa == '\0') && (*sb == '='))
|
|
return (1); /* find */
|
|
}
|
|
i++;
|
|
}
|
|
|
|
return (0); /* not find */
|
|
}
|
|
|
|
/*
|
|
* free_setattr():
|
|
* free storage pointed by "setattr"
|
|
*/
|
|
|
|
void
|
|
free_setattr(setattr)
|
|
char * setattr[];
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; setattr[i] != NULL; i++)
|
|
free(setattr[i]);
|
|
|
|
}
|
|
|
|
/*
|
|
* setup_attr():
|
|
* allocate memory and copy in attribute=value pair
|
|
* into the array of attribute=value pairs pointed to
|
|
* by "dest_attr"
|
|
*/
|
|
|
|
void
|
|
setup_attr(dest_attr, k, attr, value)
|
|
char *dest_attr[];
|
|
int k;
|
|
char attr[];
|
|
char value[];
|
|
{
|
|
if (attr != NULL) {
|
|
dest_attr[k] = (char *)calloc(PAM_MAX_ATTR_SIZE, sizeof (char));
|
|
(void) strncpy(dest_attr[k], attr, PAM_MAX_ATTR_SIZE);
|
|
(void) strncat(dest_attr[k], value, PAM_MAX_ATTR_SIZE);
|
|
} else
|
|
dest_attr[k] = NULL;
|
|
}
|
|
|
|
#ifdef PAM_NISPLUS
|
|
static char *
|
|
spskip(p)
|
|
char *p;
|
|
{
|
|
while (*p && *p != ':' && *p != '\n')
|
|
++p;
|
|
if (*p == '\n')
|
|
*p = '\0';
|
|
else if (*p)
|
|
*p++ = '\0';
|
|
return (p);
|
|
}
|
|
|
|
void
|
|
nisplus_populate_age(enobj, sp)
|
|
struct nis_object *enobj;
|
|
struct spwd *sp;
|
|
{
|
|
char *oldage, *p, *end;
|
|
long x;
|
|
|
|
/*
|
|
* shadow (col 7)
|
|
*/
|
|
|
|
sp->sp_lstchg = -1;
|
|
sp->sp_min = -1;
|
|
sp->sp_max = -1;
|
|
sp->sp_warn = -1;
|
|
sp->sp_inact = -1;
|
|
sp->sp_expire = -1;
|
|
sp->sp_flag = 0;
|
|
|
|
if ((p = ENTRY_VAL(enobj, 7)) == NULL)
|
|
return;
|
|
oldage = strdup(p);
|
|
|
|
p = oldage;
|
|
|
|
x = strtol(p, &end, 10);
|
|
if (end != memchr(p, ':', strlen(p)))
|
|
return;
|
|
if (end != p)
|
|
sp->sp_lstchg = x;
|
|
|
|
p = spskip(p);
|
|
x = strtol(p, &end, 10);
|
|
if (end != memchr(p, ':', strlen(p)))
|
|
return;
|
|
if (end != p)
|
|
sp->sp_min = x;
|
|
|
|
p = spskip(p);
|
|
x = strtol(p, &end, 10);
|
|
if (end != memchr(p, ':', strlen(p)))
|
|
return;
|
|
if (end != p)
|
|
sp->sp_max = x;
|
|
|
|
p = spskip(p);
|
|
x = strtol(p, &end, 10);
|
|
if (end != memchr(p, ':', strlen(p)))
|
|
return;
|
|
if (end != p)
|
|
sp->sp_warn = x;
|
|
|
|
p = spskip(p);
|
|
x = strtol(p, &end, 10);
|
|
if (end != memchr(p, ':', strlen(p)))
|
|
return;
|
|
if (end != p)
|
|
sp->sp_inact = x;
|
|
|
|
p = spskip(p);
|
|
x = strtol(p, &end, 10);
|
|
if (end != memchr(p, ':', strlen(p)))
|
|
return;
|
|
if (end != p)
|
|
sp->sp_expire = x;
|
|
|
|
p = spskip(p);
|
|
x = strtol(p, &end, 10);
|
|
if ((end != memchr(p, ':', strlen(p))) &&
|
|
(end != memchr(p, '\n', strlen(p))))
|
|
return;
|
|
if (end != p)
|
|
sp->sp_flag = x;
|
|
|
|
free(oldage);
|
|
}
|
|
#endif /* PAM_NISPLUS */
|
|
|
|
/*
|
|
* getloginshell() displays old login shell and asks for new login shell.
|
|
* The new login shell is then returned to calling function.
|
|
*/
|
|
char *
|
|
getloginshell(pamh, oldshell, privileged, nowarn)
|
|
pam_handle_t *pamh;
|
|
char *oldshell;
|
|
int privileged;
|
|
int nowarn;
|
|
{
|
|
char newshell[PAM_MAX_MSG_SIZE];
|
|
char *cp, *valid, *getusershell();
|
|
char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
|
|
struct pam_response *ret_resp = (struct pam_response *)0;
|
|
|
|
if (oldshell == 0 || *oldshell == '\0')
|
|
oldshell = DEFSHELL;
|
|
|
|
if (privileged == 0) {
|
|
mutex_lock(&_priv_lock);
|
|
setusershell();
|
|
for (valid = getusershell(); valid; valid = getusershell())
|
|
if (strcmp(oldshell, valid) == 0)
|
|
break;
|
|
mutex_unlock(&_priv_lock);
|
|
if (valid == NULL) {
|
|
if (!nowarn) {
|
|
sprintf(messages[0], PAM_MSG(pamh, 143,
|
|
"Cannot change from restricted shell %s"),
|
|
oldshell);
|
|
(void) __pam_display_msg(pamh, PAM_ERROR_MSG,
|
|
1, messages, NULL);
|
|
}
|
|
return (NULL);
|
|
}
|
|
}
|
|
sprintf(messages[0],
|
|
PAM_MSG(pamh, 144, "Old shell: %s"), oldshell);
|
|
(void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, messages, NULL);
|
|
sprintf(messages[0],
|
|
PAM_MSG(pamh, 145, "New shell: "));
|
|
(void) __pam_get_input(pamh, PAM_PROMPT_ECHO_ON,
|
|
1, messages, NULL, &ret_resp);
|
|
strncpy(newshell, ret_resp->resp, PAM_MAX_MSG_SIZE);
|
|
newshell[PAM_MAX_RESP_SIZE-1] = '\0';
|
|
__pam_free_resp(1, ret_resp);
|
|
|
|
cp = strchr(newshell, '\n');
|
|
if (cp)
|
|
*cp = '\0';
|
|
if (newshell[0] == '\0' || (strcmp(newshell, oldshell) == 0)) {
|
|
if (!nowarn) {
|
|
sprintf(messages[0], PAM_MSG(pamh, 146,
|
|
"Login shell unchanged."));
|
|
(void) __pam_display_msg(pamh, PAM_ERROR_MSG,
|
|
1, messages, NULL);
|
|
}
|
|
return (NULL);
|
|
}
|
|
/*
|
|
* XXX:
|
|
* Keep in mind that, for whatever this validation is worth,
|
|
* a root on a machine can edit /etc/shells and get any shell
|
|
* accepted as a valid shell in the NIS+ table.
|
|
*/
|
|
mutex_lock(&_priv_lock);
|
|
setusershell();
|
|
if (!privileged) {
|
|
for (valid = getusershell(); valid; valid = getusershell()) {
|
|
/*
|
|
* Allow user to give shell w/o preceding pathname.
|
|
*/
|
|
if (newshell[0] == '/') {
|
|
cp = valid;
|
|
} else {
|
|
cp = strrchr(valid, '/');
|
|
if (cp == 0)
|
|
cp = valid;
|
|
else
|
|
cp++;
|
|
}
|
|
if (strcmp(newshell, cp) == 0) {
|
|
strncpy(newshell, valid, strlen(valid));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
mutex_unlock(&_priv_lock);
|
|
|
|
if (newshell == 0) {
|
|
if (!nowarn) {
|
|
sprintf(messages[0], PAM_MSG(pamh, 147,
|
|
"%s is unacceptable as a new shell"),
|
|
newshell);
|
|
(void) __pam_display_msg(pamh, PAM_ERROR_MSG,
|
|
1, messages, NULL);
|
|
}
|
|
__pam_free_resp(1, ret_resp);
|
|
return (NULL);
|
|
}
|
|
if (access(newshell, X_OK) < 0) {
|
|
if (!nowarn) {
|
|
sprintf(messages[0], PAM_MSG(pamh, 148,
|
|
"warning: %s is unavailable on this machine"),
|
|
newshell);
|
|
(void) __pam_display_msg(pamh, PAM_ERROR_MSG,
|
|
1, messages, NULL);
|
|
}
|
|
}
|
|
return (strdup(newshell));
|
|
}
|
|
|
|
/*
|
|
* Get name.
|
|
*/
|
|
char *
|
|
getfingerinfo(pamh, old_gecos, nowarn)
|
|
pam_handle_t *pamh;
|
|
char *old_gecos;
|
|
int nowarn;
|
|
{
|
|
char new_gecos[PAM_MAX_MSG_SIZE];
|
|
char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
|
|
struct pam_response *ret_resp = (struct pam_response *)0;
|
|
|
|
if (!nowarn) {
|
|
sprintf(messages[0], PAM_MSG(pamh, 149,
|
|
"Default values are printed inside of '[]'."));
|
|
sprintf(messages[1], PAM_MSG(pamh, 150,
|
|
"To accept the default, type <return>."));
|
|
sprintf(messages[2], PAM_MSG(pamh, 151,
|
|
"To have a blank entry, type the word 'none'."));
|
|
(void) __pam_display_msg(pamh, PAM_TEXT_INFO,
|
|
3, messages, NULL);
|
|
}
|
|
|
|
/*
|
|
* Get name.
|
|
*/
|
|
do {
|
|
sprintf(messages[0], " ");
|
|
(void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1,
|
|
messages, NULL);
|
|
sprintf(messages[0], PAM_MSG(pamh, 152,
|
|
"Name [%s]: "), old_gecos);
|
|
(void) __pam_get_input(pamh, PAM_PROMPT_ECHO_ON,
|
|
1, messages, NULL, &ret_resp);
|
|
strncpy(new_gecos, ret_resp->resp, PAM_MAX_MSG_SIZE);
|
|
new_gecos[PAM_MAX_MSG_SIZE-1] = '\0';
|
|
__pam_free_resp(1, ret_resp);
|
|
if (special_case(new_gecos, old_gecos))
|
|
break;
|
|
} while (illegal_input(pamh, new_gecos, nowarn));
|
|
if (strcmp(new_gecos, old_gecos) == 0) {
|
|
if (!nowarn) {
|
|
sprintf(messages[0], PAM_MSG(pamh, 153,
|
|
"Finger information unchanged."));
|
|
(void) __pam_display_msg(pamh, PAM_TEXT_INFO,
|
|
1, messages, NULL);
|
|
}
|
|
return (NULL);
|
|
}
|
|
return (strdup(new_gecos));
|
|
}
|
|
|
|
/*
|
|
* Get Home Dir.
|
|
*/
|
|
char *
|
|
gethomedir(pamh, olddir, nowarn)
|
|
pam_handle_t *pamh;
|
|
char *olddir;
|
|
int nowarn;
|
|
{
|
|
char newdir[PAM_MAX_MSG_SIZE];
|
|
char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
|
|
struct pam_response *ret_resp = (struct pam_response *)0;
|
|
|
|
if (!nowarn) {
|
|
sprintf(messages[0], PAM_MSG(pamh, 149,
|
|
"Default values are printed inside of '[]'."));
|
|
sprintf(messages[1], PAM_MSG(pamh, 150,
|
|
"To accept the default, type <return>."));
|
|
sprintf(messages[2], PAM_MSG(pamh, 151,
|
|
"To have a blank entry, type the word 'none'."));
|
|
(void) __pam_display_msg(pamh, PAM_TEXT_INFO,
|
|
3, messages, NULL);
|
|
}
|
|
do {
|
|
sprintf(messages[0], " ");
|
|
(void) __pam_display_msg(pamh, PAM_TEXT_INFO,
|
|
1, messages, NULL);
|
|
sprintf(messages[0], PAM_MSG(pamh, 154,
|
|
"Home Directory [%s]: "), olddir);
|
|
(void) __pam_get_input(pamh, PAM_PROMPT_ECHO_ON,
|
|
1, messages, NULL, &ret_resp);
|
|
strncpy(newdir, ret_resp->resp, PAM_MAX_MSG_SIZE);
|
|
newdir[PAM_MAX_MSG_SIZE-1] = '\0';
|
|
__pam_free_resp(1, ret_resp);
|
|
if (special_case(newdir, olddir))
|
|
break;
|
|
} while (illegal_input(pamh, newdir, nowarn));
|
|
if (strcmp(newdir, olddir) == 0) {
|
|
if (!nowarn) {
|
|
sprintf(messages[0], PAM_MSG(pamh, 155,
|
|
"Homedir information unchanged."));
|
|
(void) __pam_display_msg(pamh, PAM_TEXT_INFO,
|
|
1, messages, NULL);
|
|
}
|
|
return (NULL);
|
|
}
|
|
return (strdup(newdir));
|
|
}
|
|
|
|
char *
|
|
repository_to_string(int repository)
|
|
{
|
|
/* if NISPLUS and FILES */
|
|
switch (repository) {
|
|
case (PAM_REP_FILES | PAM_REP_NISPLUS):
|
|
return ("files and nisplus");
|
|
case (PAM_REP_FILES | PAM_REP_NIS):
|
|
return ("files and nis");
|
|
case PAM_REP_NISPLUS:
|
|
return ("nisplus");
|
|
case PAM_REP_NIS:
|
|
return ("nis");
|
|
case PAM_REP_FILES:
|
|
return ("files");
|
|
case PAM_REP_DEFAULT:
|
|
return ("default");
|
|
default:
|
|
return ("bad repository");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Prints an error message if a ':' or a newline is found in the string.
|
|
* A message is also printed if the input string is too long.
|
|
* The password sources use :'s as separators, and are not allowed in the "gcos"
|
|
* field. Newlines serve as delimiters between users in the password source,
|
|
* and so, those too, are checked for. (I don't think that it is possible to
|
|
* type them in, but better safe than sorry)
|
|
*
|
|
* Returns '1' if a colon or newline is found or the input line is too long.
|
|
*/
|
|
static int
|
|
illegal_input(pamh, input_str, nowarn)
|
|
pam_handle_t *pamh;
|
|
char *input_str;
|
|
int nowarn;
|
|
{
|
|
char *ptr;
|
|
int error_flag = 0;
|
|
int length = (int)strlen(input_str);
|
|
char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
|
|
|
|
if (strchr(input_str, ':')) {
|
|
if (!nowarn) {
|
|
sprintf(messages[0], PAM_MSG(pamh, 156,
|
|
"':' is not allowed."));
|
|
(void) __pam_display_msg(pamh, PAM_ERROR_MSG,
|
|
1, messages, NULL);
|
|
}
|
|
error_flag = 1;
|
|
}
|
|
if (input_str[length-1] != '\n') {
|
|
/* the newline and the '\0' eat up two characters */
|
|
if (!nowarn) {
|
|
sprintf(messages[0], PAM_MSG(pamh, 157,
|
|
"Maximum number of characters allowed is %d."),
|
|
PAM_MAX_MSG_SIZE-2);
|
|
(void) __pam_display_msg(pamh, PAM_ERROR_MSG,
|
|
1, messages, NULL);
|
|
}
|
|
error_flag = 1;
|
|
}
|
|
/*
|
|
* Delete newline by shortening string by 1.
|
|
*/
|
|
input_str[length-1] = '\0';
|
|
/*
|
|
* Don't allow control characters, etc in input string.
|
|
*/
|
|
for (ptr = input_str; *ptr != '\0'; ptr++) {
|
|
/* 040 is ascii char "space" */
|
|
if ((int) *ptr < 040) {
|
|
if (!nowarn) {
|
|
sprintf(messages[0], PAM_MSG(pamh, 158,
|
|
"Control characters are not allowed."));
|
|
(void) __pam_display_msg(pamh, PAM_ERROR_MSG,
|
|
1, messages, NULL);
|
|
}
|
|
error_flag = 1;
|
|
break;
|
|
}
|
|
}
|
|
return (error_flag);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* special_case returns true when either the default is accepted
|
|
* (str = '\n'), or when 'none' is typed. 'none' is accepted in
|
|
* either upper or lower case (or any combination). 'str' is modified
|
|
* in these two cases.
|
|
*/
|
|
static int
|
|
special_case(str, default_str)
|
|
char *str, *default_str;
|
|
{
|
|
static char word[] = "none\n";
|
|
char *ptr, *wordptr;
|
|
|
|
/*
|
|
* If the default is accepted, then change the old string do the
|
|
* default string.
|
|
*/
|
|
if (*str == '\n') {
|
|
(void) strcpy(str, default_str);
|
|
return (1);
|
|
}
|
|
/*
|
|
* Check to see if str is 'none'. (It is questionable if case
|
|
* insensitivity is worth the hair).
|
|
*/
|
|
wordptr = word - 1;
|
|
for (ptr = str; *ptr != '\0'; ++ptr) {
|
|
++wordptr;
|
|
if (*wordptr == '\0') /* then words are different sizes */
|
|
return (0);
|
|
if (*ptr == *wordptr)
|
|
continue;
|
|
if (isupper(*ptr) && (tolower(*ptr) == *wordptr))
|
|
continue;
|
|
/*
|
|
* At this point we have a mismatch, so we return
|
|
*/
|
|
return (0);
|
|
}
|
|
/*
|
|
* Make sure that words are the same length.
|
|
*/
|
|
if (*(wordptr+1) != '\0')
|
|
return (0);
|
|
/*
|
|
* Change 'str' to be the null string
|
|
*/
|
|
*str = '\0';
|
|
return (1);
|
|
}
|
|
|
|
static int
|
|
copy_passwd_structs(struct passwd **pwd, struct passwd *local_pwd,
|
|
struct spwd **shpwd, struct spwd *local_shpwd)
|
|
{
|
|
|
|
/* copy the passwd information */
|
|
if ((*pwd = (struct passwd *)
|
|
calloc(1, sizeof (struct passwd))) == NULL)
|
|
return (PAM_BUF_ERR);
|
|
if (local_pwd->pw_name) {
|
|
if (((*pwd)->pw_name = strdup(local_pwd->pw_name)) == NULL)
|
|
goto out;
|
|
}
|
|
if (local_pwd->pw_passwd) {
|
|
if (((*pwd)->pw_passwd = strdup(local_pwd->pw_passwd)) == NULL)
|
|
goto out;
|
|
}
|
|
|
|
(*pwd)->pw_uid = local_pwd->pw_uid;
|
|
(*pwd)->pw_gid = local_pwd->pw_gid;
|
|
|
|
if (local_pwd->pw_gecos) {
|
|
if (((*pwd)->pw_gecos = strdup(local_pwd->pw_gecos)) == NULL)
|
|
goto out;
|
|
}
|
|
if (local_pwd->pw_dir) {
|
|
if (((*pwd)->pw_dir = strdup(local_pwd->pw_dir)) == NULL)
|
|
goto out;
|
|
}
|
|
if (local_pwd->pw_shell) {
|
|
if (((*pwd)->pw_shell = strdup(local_pwd->pw_shell)) == NULL)
|
|
goto out;
|
|
}
|
|
|
|
/* copy the shadow passwd information */
|
|
if ((*shpwd = (struct spwd *)
|
|
calloc(1, sizeof (struct spwd))) == NULL)
|
|
goto out;
|
|
**shpwd = *local_shpwd;
|
|
if (local_shpwd->sp_namp) {
|
|
if (((*shpwd)->sp_namp = strdup(local_shpwd->sp_namp)) == NULL)
|
|
goto out;
|
|
}
|
|
if (local_shpwd->sp_pwdp) {
|
|
if (((*shpwd)->sp_pwdp = strdup(local_shpwd->sp_pwdp)) == NULL)
|
|
goto out;
|
|
}
|
|
|
|
return (PAM_SUCCESS);
|
|
out:
|
|
free_passwd_structs(*pwd, *shpwd);
|
|
return (PAM_BUF_ERR);
|
|
}
|
|
|
|
void
|
|
free_passwd_structs(struct passwd *pwd, struct spwd *shpwd)
|
|
{
|
|
if (pwd) {
|
|
if (pwd->pw_name)
|
|
free(pwd->pw_name);
|
|
if (pwd->pw_passwd) {
|
|
memset(pwd->pw_passwd, 0, strlen(pwd->pw_passwd));
|
|
free(pwd->pw_passwd);
|
|
}
|
|
if (pwd->pw_gecos)
|
|
free(pwd->pw_gecos);
|
|
if (pwd->pw_dir)
|
|
free(pwd->pw_dir);
|
|
if (pwd->pw_shell)
|
|
free(pwd->pw_shell);
|
|
free(pwd);
|
|
}
|
|
|
|
if (shpwd) {
|
|
if (shpwd->sp_namp)
|
|
free(shpwd->sp_namp);
|
|
if (shpwd->sp_pwdp) {
|
|
memset(shpwd->sp_pwdp, 0, strlen(shpwd->sp_pwdp));
|
|
free(shpwd->sp_pwdp);
|
|
}
|
|
free(shpwd);
|
|
}
|
|
}
|