/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #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); } }