#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(const char *name, attrlist *item, pwu_repository_t *rep);
int nis_getpwnam(const char *name, attrlist *items, pwu_repository_t *rep,
void **buf);
int nis_update(attrlist *items, pwu_repository_t *rep, void *buf);
int nis_putpwnam(const char *name, const char *oldpw, pwu_repository_t *rep,
void *buf);
int nis_user_to_authenticate(const char *user, pwu_repository_t *rep,
char **auth_user, int *privileged);
struct repops nis_repops = {
NULL,
nis_getattr,
nis_getpwnam,
nis_update,
nis_putpwnam,
nis_user_to_authenticate,
NULL,
NULL
};
typedef struct {
char *domain;
char *master;
char *scratch;
int scratchlen;
char *c2scratch;
int c2scratchlen;
struct passwd *pwd;
} nisbuf_t;
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);
return (getuid() == 0);
}
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';
}
int
nis_user_to_authenticate(const char *user, pwu_repository_t *rep,
char **auth_user, int *privileged)
{
nisbuf_t *buf = NULL;
int res;
attrlist attr_tmp[1];
uid_t uid;
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);
if (uid == 0 || uid == buf->pwd->pw_uid) {
*auth_user = strdup(user);
res = PWU_SUCCESS;
} else {
res = PWU_DENIED;
}
}
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);
}
int
nis_getattr(const 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;
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;
break;
default:
break;
}
}
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);
}
int
nis_getpwnam(const char *name, attrlist *items, pwu_repository_t *rep,
void **buf)
{
nisbuf_t *nisbuf;
int nisresult;
char *ncname;
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);
}
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");
free(nisbuf->master);
free(nisbuf->pwd);
free(nisbuf);
return (PWU_SERVER_ERROR);
}
ncname = strdup(name);
if (ncname == NULL) {
free(nisbuf->master);
free(nisbuf->pwd);
free(nisbuf);
return (PWU_NOMEM);
}
nisresult = yp_match(nisbuf->domain, "passwd.byname", ncname,
strlen(ncname), &(nisbuf->scratch),
&(nisbuf->scratchlen));
free(ncname);
if (nisresult != 0) {
free(nisbuf->pwd);
free(nisbuf->scratch);
free(nisbuf->master);
free(nisbuf);
return (PWU_NOT_FOUND);
}
nis_to_pwd(nisbuf->scratch, nisbuf->pwd);
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) {
p = nisbuf->c2scratch;
(void) strsep(&p, ":");
nisbuf->pwd->pw_passwd = strsep(&p, ":");
}
}
*buf = (void *)nisbuf;
return (PWU_SUCCESS);
}
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;
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 {
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);
}
int
nis_putpwnam(const char *name, const 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;
char *oldpass;
if (strcmp(name, "root") == 0)
return (PWU_NOT_FOUND);
oldpass = yppasswd.oldpass = strdup(oldpw != NULL ? oldpw : "");
if (oldpass == NULL)
return (PWU_NOMEM);
yppasswd.newpw = *nisbuf->pwd;
if (nis_privileged(nisbuf)) {
nconf = getnetconfigent("ticlts");
if (!nconf) {
free(oldpass);
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 {
client = clnt_create(nisbuf->master, YPPASSWDPROG,
YPPASSWDVERS, "udp6");
if (client == NULL)
client = clnt_create(nisbuf->master, YPPASSWDPROG,
YPPASSWDVERS, "udp");
}
if (client == NULL) {
free(oldpass);
syslog(LOG_ERR,
"passwdutil.so: couldn't create client to YP master");
return (PWU_SERVER_ERROR);
}
timeout.tv_usec = 0;
timeout.tv_sec = 55;
ans = CLNT_CALL(client, YPPASSWDPROC_UPDATE, xdr_yppasswd,
(char *)&yppasswd, xdr_int, (char *)&ok, timeout);
free(oldpass);
free(nisbuf->pwd);
free(nisbuf->master);
free(nisbuf->scratch);
free(nisbuf->c2scratch);
(void) clnt_destroy(client);
if (ans != RPC_SUCCESS) {
return (PWU_UPDATE_FAILED);
}
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);
}
}