OpenSolaris_b135/lib/passwdutil/nis_attr.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/errno.h>
#include <pwd.h>
#include <unistd.h>
#include <syslog.h>

#include <netdb.h>

#include <rpc/rpc.h>
#include <rpcsvc/yppasswd.h>
#include <rpcsvc/ypclnt.h>
#include <rpcsvc/yp_prot.h>

#include "passwdutil.h"

int nis_getattr(char *name, attrlist *item, pwu_repository_t *rep);
int nis_getpwnam(char *name, attrlist *items, pwu_repository_t *rep,
    void **buf);
int nis_update(attrlist *items, pwu_repository_t *rep, void *buf);
int nis_putpwnam(char *name, char *oldpw, pwu_repository_t *rep, void *buf);
int nis_user_to_authenticate(char *user, pwu_repository_t *rep,
	char **auth_user, int *privileged);

/*
 * nis function pointer table, used by passwdutil_init to initialize
 * the global Repository-OPerations table "rops"
 */
struct repops nis_repops = {
	NULL,	/* checkhistory */
	nis_getattr,
	nis_getpwnam,
	nis_update,
	nis_putpwnam,
	nis_user_to_authenticate,
	NULL,	/* lock */
	NULL	/* unlock */
};

/*
 * structure used to keep state between get/update/put calls
 */
typedef struct {
	char *domain;
	char *master;
	char *scratch;
	int scratchlen;
	char *c2scratch;
	int c2scratchlen;
	struct passwd *pwd;
} nisbuf_t;

/*
 * Are we a 'privileged' process? Yes if we are running on the
 * NIS server AND we are root...
 */
int
nis_privileged(nisbuf_t *nisbuf)
{
	char thishost[MAXHOSTNAMELEN];
	if (gethostname(thishost, sizeof (thishost)) == -1) {
		syslog(LOG_ERR, "passwdutil.so: Can't get hostname");
		return (0);
	}

	if (strcmp(nisbuf->master, thishost) != 0)
		return (0);

	/* We're running on the NIS server. */
	return (getuid() == 0);
}

/*
 * nis_to_pwd()
 *
 * convert password-entry-line to "struct passwd"
 */
void
nis_to_pwd(char *nis, struct passwd *pwd)
{
	pwd->pw_name = strsep(&nis, ":");
	pwd->pw_passwd = strsep(&nis, ":");
	pwd->pw_uid = atoi(strsep(&nis, ":"));
	pwd->pw_gid = atoi(strsep(&nis, ":"));
	pwd->pw_gecos = strsep(&nis, ":");
	pwd->pw_dir = strsep(&nis, ":");
	pwd->pw_shell = nis;
	if (pwd->pw_shell[0])
		pwd->pw_shell[strlen(pwd->pw_shell)-1] = '\0';
}

/*
 * nis_user_to_authenticate(name, rep, auth_user, privileged)
 *
 */
/*ARGSUSED*/
int
nis_user_to_authenticate(char *user, pwu_repository_t *rep,
	char **auth_user, int *privileged)
{
	nisbuf_t *buf = NULL;
	int res;
	attrlist attr_tmp[1];
	uid_t uid;

	/*
	 * special NIS case: don't bother to get "root" from NIS
	 */
	if (strcmp(user, "root") == 0)
		return (PWU_NOT_FOUND);

	attr_tmp[0].type = ATTR_UID;
	attr_tmp[0].next = NULL;

	res = nis_getpwnam(user, &attr_tmp[0], rep, (void **)&buf);

	if (res != PWU_SUCCESS)
		return (res);

	if (nis_privileged(buf)) {
		*privileged = 1;
		*auth_user = NULL;
		res = PWU_SUCCESS;
	} else {
		uid = getuid();

		*privileged = (uid == (uid_t)0);

		/* root, or user herself can change attributes */
		if (uid == 0 || uid == buf->pwd->pw_uid) {
			*auth_user = strdup(user);
			res = PWU_SUCCESS;
		} else {
			res = PWU_DENIED;
		}
	}

	/*
	 * Do not release buf->domain.
	 * It's been set by yp_get_default_domain()
	 * and must not be freed.
	 * See man page yp_get_default_domain(3NSL)
	 * for details.
	 */
	if (buf->master)
		free(buf->master);
	if (buf->scratch)
		free(buf->scratch);
	if (buf->c2scratch)
		free(buf->c2scratch);
	free(buf->pwd);
	free(buf);

	return (res);
}


/*
 * nis_getattr(name, items, rep)
 *
 * get account attributes specified in 'items'
 */
int
nis_getattr(char *name, attrlist *items, pwu_repository_t *rep)
{
	nisbuf_t *nisbuf = NULL;
	struct passwd *pw;
	attrlist *w;
	int res;

	res = nis_getpwnam(name, items, rep, (void **)&nisbuf);
	if (res != PWU_SUCCESS)
		return (res);

	pw = nisbuf->pwd;

	for (w = items; w != NULL; w = w->next) {
		switch (w->type) {
		case ATTR_NAME:
			if ((w->data.val_s = strdup(pw->pw_name)) == NULL)
				res = PWU_NOMEM;
			break;
		case ATTR_COMMENT:
			if ((w->data.val_s = strdup(pw->pw_comment)) == NULL)
				res = PWU_NOMEM;
			break;
		case ATTR_GECOS:
			if ((w->data.val_s = strdup(pw->pw_gecos)) == NULL)
				res = PWU_NOMEM;
			break;
		case ATTR_HOMEDIR:
			if ((w->data.val_s = strdup(pw->pw_dir)) == NULL)
				res = PWU_NOMEM;
			break;
		case ATTR_SHELL:
			if ((w->data.val_s = strdup(pw->pw_shell)) == NULL)
				res = PWU_NOMEM;
			break;
		case ATTR_PASSWD:
		case ATTR_PASSWD_SERVER_POLICY:
			if ((w->data.val_s = strdup(pw->pw_passwd)) == NULL)
				res = PWU_NOMEM;
			break;
		case ATTR_REP_NAME:
			if ((w->data.val_s = strdup("nis")) == NULL)
				res = PWU_NOMEM;
			break;

		/* integer values */
		case ATTR_UID:
			w->data.val_i = nisbuf->pwd->pw_uid;
			break;
		case ATTR_GID:
			w->data.val_i = nisbuf->pwd->pw_gid;
			break;
		case ATTR_LSTCHG:
		case ATTR_MIN:
		case ATTR_MAX:
		case ATTR_WARN:
		case ATTR_INACT:
		case ATTR_EXPIRE:
		case ATTR_FLAG:
		case ATTR_AGE:
			w->data.val_i = -1;	/* not used for NIS */
			break;
		default:
			break;
		}
	}

	/*
	 * Do not release nisbuf->domain.
	 * It's been set by yp_get_default_domain()
	 * and must not be freed.
	 * See man page yp_get_default_domain(3NSL)
	 * for details.
	 */
	if (nisbuf->master)
		free(nisbuf->master);
	if (nisbuf->scratch)
		free(nisbuf->scratch);
	if (nisbuf->c2scratch)
		free(nisbuf->c2scratch);
	free(nisbuf->pwd);
	free(nisbuf);

	return (res);
}

/*
 * nis_getpwnam(name, items, rep)
 *
 * Get the account information of user 'name'
 */
/*ARGSUSED*/
int
nis_getpwnam(char *name, attrlist *items, pwu_repository_t *rep,
    void **buf)
{
	nisbuf_t *nisbuf;
	int nisresult;

	nisbuf = calloc(sizeof (*nisbuf), 1);
	if (nisbuf == NULL)
		return (PWU_NOMEM);

	nisbuf->pwd = malloc(sizeof (struct passwd));
	if (nisbuf->pwd == NULL) {
		free(nisbuf);
		return (PWU_NOMEM);
	}

	/*
	 * Do not release nisbuf->domain.
	 * It is going to be set by yp_get_default_domain()
	 * and must not be freed.
	 * See man page yp_get_default_domain(3NSL)
	 * for details.
	 */
	if (yp_get_default_domain(&nisbuf->domain) != 0) {
		syslog(LOG_ERR, "passwdutil.so: can't get domain");
		free(nisbuf->pwd);
		free(nisbuf);
		return (PWU_SERVER_ERROR);
	}

	if (yp_master(nisbuf->domain, "passwd.byname", &nisbuf->master) != 0) {
		syslog(LOG_ERR,
		    "passwdutil.so: can't get master for passwd map");
		if (nisbuf->master)
			free(nisbuf->master);
		free(nisbuf->pwd);
		free(nisbuf);
		return (PWU_SERVER_ERROR);
	}

	nisresult = yp_match(nisbuf->domain, "passwd.byname", name,
	    strlen(name), &(nisbuf->scratch),
	    &(nisbuf->scratchlen));
	if (nisresult != 0) {
		(void) free(nisbuf->pwd);
		if (nisbuf->scratch)
			(void) free(nisbuf->scratch);
		if (nisbuf->master)
			(void) free(nisbuf->master);
		(void) free(nisbuf);
		return (PWU_NOT_FOUND);
	}

	nis_to_pwd(nisbuf->scratch, nisbuf->pwd);

	/*
	 * check for the C2 security flag "##" in the passwd field.
	 * If the first 2 chars in the passwd field is "##", get
	 * the user's passwd from passwd.adjunct.byname map.
	 * The lookup to this passwd.adjunct.byname map will only
	 * succeed if the caller's uid is 0 because only root user
	 * can use privilege port.
	 */
	if (nisbuf->pwd->pw_passwd[0] == '#' &&
	    nisbuf->pwd->pw_passwd[1] == '#') {
		char *key = &nisbuf->pwd->pw_passwd[2];
		int keylen;
		char *p;

		keylen = strlen(key);

		nisresult = yp_match(nisbuf->domain, "passwd.adjunct.byname",
		    key, keylen, &(nisbuf->c2scratch),
		    &(nisbuf->c2scratchlen));

		if (nisresult == 0 && nisbuf->c2scratch != NULL) {
			/* Skip username (first field), and pick up password */
			p = nisbuf->c2scratch;
			(void) strsep(&p, ":");
			nisbuf->pwd->pw_passwd = strsep(&p, ":");
		}
	}

	*buf = (void *)nisbuf;

	return (PWU_SUCCESS);
}

/*
 * nis_update(items, rep, buf)
 *
 * update the information in "buf" with the attribute/values
 * specified in "items".
 */
/*ARGSUSED*/
int
nis_update(attrlist *items, pwu_repository_t *rep, void *buf)
{
	attrlist *p;
	nisbuf_t *nisbuf = (nisbuf_t *)buf;
	char *salt;

	for (p = items; p != NULL; p = p->next) {
		switch (p->type) {
		case ATTR_NAME:
			break;
		/*
		 * Nothing special needs to be done for
		 * server policy
		 */
		case ATTR_PASSWD:
		case ATTR_PASSWD_SERVER_POLICY:
			salt = crypt_gensalt(
			    nisbuf->pwd->pw_passwd, nisbuf->pwd);

			if (salt == NULL) {
				if (errno == ENOMEM)
					return (PWU_NOMEM);
				else {
					/* algorithm problem? */
					syslog(LOG_AUTH | LOG_ALERT,
					    "passwdutil: crypt_gensalt "
					    "%m");
					return (PWU_UPDATE_FAILED);
				}
			}
			nisbuf->pwd->pw_passwd = crypt(p->data.val_s, salt);
			free(salt);
			break;
		case ATTR_UID:
			nisbuf->pwd->pw_uid = (uid_t)p->data.val_i;
			break;
		case ATTR_GID:
			nisbuf->pwd->pw_gid = (gid_t)p->data.val_i;
			break;
		case ATTR_AGE:
			nisbuf->pwd->pw_age = p->data.val_s;
			break;
		case ATTR_COMMENT:
			nisbuf->pwd->pw_comment = p->data.val_s;
			break;
		case ATTR_GECOS:
			nisbuf->pwd->pw_gecos = p->data.val_s;
			break;
		case ATTR_HOMEDIR:
			nisbuf->pwd->pw_dir = p->data.val_s;
			break;
		case ATTR_SHELL:
			nisbuf->pwd->pw_shell = p->data.val_s;
			break;
		case ATTR_LSTCHG:
		case ATTR_MIN:
		case ATTR_MAX:
		case ATTR_WARN:
		case ATTR_INACT:
		case ATTR_EXPIRE:
		case ATTR_FLAG:
		default:
			break;
		}
	}
	return (PWU_SUCCESS);
}

/*
 * nis_putpwnam(name, oldpw, rep, buf)
 *
 * Update the NIS server. The passwd structure in buf will be sent to
 * the server for user "name" authenticating with password "oldpw".
 */
/*ARGSUSED*/
int
nis_putpwnam(char *name, char *oldpw, pwu_repository_t *rep,
	void *buf)
{
	nisbuf_t *nisbuf = (nisbuf_t *)buf;
	struct yppasswd yppasswd;
	struct netconfig *nconf;
	int ok;
	enum clnt_stat ans;
	CLIENT *client;
	struct timeval timeout;

	if (strcmp(name, "root") == 0)
		return (PWU_NOT_FOUND);

	yppasswd.oldpass = oldpw ? oldpw : "";
	yppasswd.newpw = *nisbuf->pwd;

	/*
	 * If we are privileged, we create a ticlts connection to the
	 * NIS server so that it can check our credentials
	 */
	if (nis_privileged(nisbuf)) {
		nconf = getnetconfigent("ticlts");
		if (!nconf) {
			syslog(LOG_ERR,
			    "passwdutil.so: Couldn't get netconfig entry");
			return (PWU_SYSTEM_ERROR);
		}
		client = clnt_tp_create(nisbuf->master, YPPASSWDPROG,
		    YPPASSWDVERS, nconf);
		freenetconfigent(nconf);
	} else {
		/* Try IPv6 first */
		client = clnt_create(nisbuf->master, YPPASSWDPROG,
		    YPPASSWDVERS, "udp6");
		if (client == NULL)
			client = clnt_create(nisbuf->master, YPPASSWDPROG,
			    YPPASSWDVERS, "udp");
	}

	if (client == NULL) {
		syslog(LOG_ERR,
		    "passwdutil.so: couldn't create client to YP master");
		return (PWU_SERVER_ERROR);
	}

	timeout.tv_usec = 0;
	timeout.tv_sec = 55;	/* ndp uses 55 seconds */

	ans = CLNT_CALL(client, YPPASSWDPROC_UPDATE, xdr_yppasswd,
	    (char *)&yppasswd, xdr_int, (char *)&ok, timeout);

	if (nisbuf->pwd)
		(void) free(nisbuf->pwd);
	if (nisbuf->master)
		(void) free(nisbuf->master);
	if (nisbuf->scratch)
		(void) free(nisbuf->scratch);
	if (nisbuf->c2scratch)
		(void) free(nisbuf->c2scratch);

	(void) clnt_destroy(client);

	if (ans != RPC_SUCCESS) {
		return (PWU_UPDATE_FAILED);
	}

	/* These errors are obtained from the yppasswdd.c code */
	switch (ok) {
		case 2: return (PWU_DENIED);
		case 8: return (PWU_BUSY);
		case 9: return (PWU_SERVER_ERROR);
		case 4: return (PWU_NOT_FOUND);
		case 3: return (PWU_NO_CHANGE);
		case 7: return (PWU_DENIED);
		case 0: return (PWU_SUCCESS);
		default: return (PWU_SYSTEM_ERROR);
	}
}