3 * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Bill Paul.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * NIS interface routines for chpass
34 * Written by Bill Paul <wpaul@ctr.columbia.edu>
35 * Center for Telecommunications Research
36 * Columbia University, New York City
38 * $FreeBSD: src/usr.bin/chpass/pw_yp.c,v 1.16.2.1 2002/02/15 00:46:56 des Exp $
39 * $DragonFly: src/usr.bin/chpass/pw_yp.c,v 1.5 2005/05/07 22:29:39 corecode Exp $
48 #include <sys/types.h>
57 #include <sys/types.h>
59 #include <sys/param.h>
62 #include <rpcsvc/yp.h>
63 struct dom_binding {};
64 #include <rpcsvc/ypclnt.h>
65 #include <rpcsvc/yppasswd.h>
68 #include "ypxfr_extern.h"
69 #include "yppasswd_private.h"
71 #define PERM_SECURE (S_IRUSR|S_IWUSR)
76 2048 * 1024, /* cachesize */
83 int suser_override = 0;
84 int yp_in_pw_file = 0;
85 char *yp_domain = NULL;
86 char *yp_server = NULL;
88 extern char *tempname;
90 /* Save the local and NIS password information */
91 struct passwd local_password;
92 struct passwd yp_password;
94 void copy_yp_pass(char *p, int x, int m)
99 yp_password.pw_fields = 0;
101 buf = (char *)realloc(buf, m + 10);
104 /* Turn all colons into NULLs */
105 while (strchr(s, ':')) {
106 s = (strchr(s, ':') + 1);
111 #define EXPAND(e) e = t; while ((*t++ = *p++));
112 EXPAND(yp_password.pw_name);
113 yp_password.pw_fields |= _PWF_NAME;
114 EXPAND(yp_password.pw_passwd);
115 yp_password.pw_fields |= _PWF_PASSWD;
116 yp_password.pw_uid = atoi(p);
117 p += (strlen(p) + 1);
118 yp_password.pw_fields |= _PWF_UID;
119 yp_password.pw_gid = atoi(p);
120 p += (strlen(p) + 1);
121 yp_password.pw_fields |= _PWF_GID;
123 EXPAND(yp_password.pw_class);
124 yp_password.pw_fields |= _PWF_CLASS;
125 yp_password.pw_change = atol(p);
126 p += (strlen(p) + 1);
127 yp_password.pw_fields |= _PWF_CHANGE;
128 yp_password.pw_expire = atol(p);
129 p += (strlen(p) + 1);
130 yp_password.pw_fields |= _PWF_EXPIRE;
132 EXPAND(yp_password.pw_gecos);
133 yp_password.pw_fields |= _PWF_GECOS;
134 EXPAND(yp_password.pw_dir);
135 yp_password.pw_fields |= _PWF_DIR;
136 EXPAND(yp_password.pw_shell);
137 yp_password.pw_fields |= _PWF_SHELL;
142 void copy_local_pass(char *p, int m)
147 buf = (char *)realloc(buf, m + 10);
151 EXPAND(local_password.pw_name);
152 EXPAND(local_password.pw_passwd);
153 bcopy(p, (char *)&local_password.pw_uid, sizeof(int));
155 bcopy(p, (char *)&local_password.pw_gid, sizeof(int));
157 bcopy(p, (char *)&local_password.pw_change, sizeof(time_t));
159 EXPAND(local_password.pw_class);
160 EXPAND(local_password.pw_gecos);
161 EXPAND(local_password.pw_dir);
162 EXPAND(local_password.pw_shell);
163 bcopy(p, (char *)&local_password.pw_expire, sizeof(time_t));
165 bcopy(p, (char *)&local_password.pw_fields, sizeof local_password.pw_fields);
166 p += sizeof local_password.pw_fields;
172 * It is not mandatory that an NIS master server also be a client.
173 * However, if the NIS master is not configured as a client, then the
174 * domain name will not be set and ypbind will not be running, so we
175 * will be unable to use the ypclnt routines inside libc. We therefore
176 * need our own magic version of yp_match() which we can use in any
179 static int my_yp_match(char *server, char *domain, char *map, char *key,
180 unsigned long keylen, char **result, unsigned long *resultlen)
185 static char buf[YPMAXRECORD + 2];
187 bzero((char *)buf, sizeof(buf));
190 * Don't make this a fatal error. The inability to contact
191 * a server is, for our purposes, equivalent to not finding
192 * the record we were looking for. Letting use_yp() know
193 * that the lookup failed is sufficient.
195 if ((clnt = clnt_create(server, YPPROG,YPVERS,"udp")) == NULL) {
198 warnx("failed to create UDP handle: %s",
199 clnt_spcreateerror(server));
200 pw_error(tempname, 0, 1);
204 ypkey.domain = domain;
206 ypkey.key.keydat_len = keylen;
207 ypkey.key.keydat_val = key;
209 if ((ypval = ypproc_match_2(&ypkey, clnt)) == NULL) {
213 warnx("%s",clnt_sperror(clnt,"YPPROC_MATCH failed"));
214 pw_error(tempname, 0, 1);
220 if (ypval->stat != YP_TRUE) {
221 xdr_free(xdr_ypresp_val, (char *)ypval);
224 int stat = ypval->stat;
225 xdr_free(xdr_ypresp_val, (char *)ypval);
226 if (stat == YP_NOMAP && strstr(map, "master.passwd"))
228 if (stat == YP_NOKEY)
230 warnx("ypmatch failed: %s", yperr_string(ypprot_err(stat)));
231 pw_error(tempname, 0, 1);
236 strncpy((char *)&buf, ypval->val.valdat_val, ypval->val.valdat_len);
238 *result = (char *)&buf;
239 *resultlen = ypval->val.valdat_len;
241 xdr_free(xdr_ypresp_val, (char *)ypval);
247 * Check if the user we're working with is local or in NIS.
249 /* which: 0 = use username, 1 = use uid */
250 int use_yp (char *user, uid_t uid, int which)
252 int user_local = 0, user_yp = 0, user_exists = 0;
255 char bf[UT_NAMESIZE + 2];
259 char ubuf[UT_NAMESIZE + 2];
262 snprintf(ubuf, sizeof(ubuf), "%lu", (unsigned long)uid);
263 user = (char *)&ubuf;
266 /* Grope around for the user in the usual way */
268 if (getpwuid(uid) != NULL)
271 if (getpwnam(user) != NULL)
275 /* Now grope directly through the user database */
276 if ((dbp = dbopen(_PATH_SMP_DB, O_RDONLY, PERM_SECURE,
277 DB_HASH, &openinfo)) == NULL) {
278 warn("error opening database: %s.", _PATH_MP_DB);
279 pw_error(tempname, 0, 1);
282 /* Is NIS turned on */
283 bf[0] = _PW_KEYYPENABLED;
284 key.data = (u_char *)bf;
286 yp_in_pw_file = !(dbp->get)(dbp,&key,&data,0);
287 if (_yp_check(NULL) || (yp_domain && yp_server)) {
288 server = get_yp_master(0);
290 /* Is the user in the NIS passwd map */
291 if (!my_yp_match(server, yp_domain, which ? "passwd.byuid" :
292 "passwd.byname", user, strlen(user),
293 &result, &resultlen)) {
294 user_yp = user_exists = 1;
295 *(char *)(result + resultlen) = '\0';
296 copy_yp_pass(result, 0, resultlen);
298 /* Is the user in the NIS master.passwd map */
299 if (user_yp && !my_yp_match(server, yp_domain, which ?
300 "master.passwd.byuid" : "master.passwd.byname",
302 &result, &resultlen)) {
303 *(char *)(result + resultlen) = '\0';
304 copy_yp_pass(result, 1, resultlen);
308 /* Is the user in the local password database */
310 bf[0] = which ? _PW_KEYBYUID : _PW_KEYBYNAME;
312 bcopy((char *)&uid, bf + 1, sizeof(uid));
314 bcopy((char *)user, bf + 1, MIN(strlen(user), UT_NAMESIZE));
315 key.data = (u_char *)bf;
316 key.size = which ? sizeof(uid) + 1 : strlen(user) + 1;
317 if (!(dbp->get)(dbp,&key,&data,0)) {
319 copy_local_pass(data.data, data.size);
324 if (user_local && user_yp && user_exists)
325 return(USER_YP_AND_LOCAL);
326 else if (!user_local && user_yp && user_exists)
327 return(USER_YP_ONLY);
328 else if (user_local && !user_yp && user_exists)
329 return(USER_LOCAL_ONLY);
330 else if (!user_exists)
331 return(USER_UNKNOWN);
337 * Find the name of the NIS master server for this domain
338 * and make sure it's running yppasswdd.
340 char *get_yp_master(int getserver)
345 char *sockname = YP_SOCKNAME;
348 * Sometimes we are called just to probe for rpc.yppasswdd and
349 * set the suser_override flag. Just return NULL and leave
350 * suser_override at 0 if _use_yp doesn't indicate that NIS is
351 * in use and we weren't called from use_yp() itself.
352 * Without this check, we might try probing and fail with an NIS
353 * error in non-NIS environments.
355 if ((_use_yp == USER_UNKNOWN || _use_yp == USER_LOCAL_ONLY) &&
359 /* Get default NIS domain. */
361 if (yp_domain == NULL && (rval = yp_get_default_domain(&yp_domain))) {
362 warnx("can't get local NIS domain name: %s",yperr_string(rval));
363 pw_error(tempname, 0, 1);
366 /* Get master server of passwd map. */
368 if ((mastername = ypxfr_get_master(yp_domain, "passwd.byname",
369 yp_server, yp_server ? 0 : 1)) == NULL) {
370 warnx("can't get name of master NIS server");
371 pw_error(tempname, 0, 1);
377 /* Check if yppasswdd is out there. */
379 if ((rval = getrpcport(mastername, YPPASSWDPROG, YPPASSWDPROC_UPDATE,
380 IPPROTO_UDP)) == 0) {
381 warnx("rpc.yppasswdd is not running on the NIS master server");
382 pw_error(tempname, 0, 1);
386 * Make sure it's on a reserved port.
387 * XXX Might break with yppasswdd servers running on Solaris 2.x.
390 if (rval >= IPPORT_RESERVED) {
391 warnx("rpc.yppasswdd server not running on reserved port");
392 pw_error(tempname, 0, 1);
395 /* See if _we_ are the master server. */
396 if (!force_old && !getuid() && (localport = getrpcport("localhost",
397 YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP)) != 0) {
398 if (localport == rval && stat(sockname, &st) != -1) {
400 mastername = "localhost";
404 /* Everything checks out: return the name of the server. */
410 * Ask the user for his NIS password and submit the new information
411 * to yppasswdd. Note that rpc.yppasswdd requires password authentication
412 * and only allows changes to existing records rather than the addition
413 * of new records. (To do actual updates we would need something like
414 * secure RPC and ypupdated, which FreeBSD doesn't have yet.) The FreeBSD
415 * rpc.yppasswdd has some special hooks to allow the superuser update
416 * information without specifying a password, however this only works
417 * for the superuser on the NIS master server.
419 void yp_submit(struct passwd *pw)
421 struct yppasswd yppasswd;
422 struct master_yppasswd master_yppasswd;
424 char *master, *password;
427 char *sockname = YP_SOCKNAME;
431 /* Get NIS master server name */
433 master = get_yp_master(1);
435 /* Populate the yppasswd structure that gets handed to yppasswdd. */
437 if (suser_override) {
438 master_yppasswd.newpw.pw_passwd = strdup(pw->pw_passwd);
439 master_yppasswd.newpw.pw_name = strdup(pw->pw_name);
440 master_yppasswd.newpw.pw_uid = pw->pw_uid;
441 master_yppasswd.newpw.pw_gid = pw->pw_gid;
442 master_yppasswd.newpw.pw_expire = pw->pw_expire;
443 master_yppasswd.newpw.pw_change = pw->pw_change;
444 master_yppasswd.newpw.pw_fields = pw->pw_fields;
445 master_yppasswd.newpw.pw_gecos = strdup(pw->pw_gecos);
446 master_yppasswd.newpw.pw_dir = strdup(pw->pw_dir);
447 master_yppasswd.newpw.pw_shell = strdup(pw->pw_shell);
448 master_yppasswd.newpw.pw_class = pw->pw_class != NULL ?
449 strdup(pw->pw_class) : "";
450 master_yppasswd.oldpass = ""; /* not really needed */
451 master_yppasswd.domain = yp_domain;
453 yppasswd.newpw.pw_passwd = strdup(pw->pw_passwd);
454 yppasswd.newpw.pw_name = strdup(pw->pw_name);
455 yppasswd.newpw.pw_uid = pw->pw_uid;
456 yppasswd.newpw.pw_gid = pw->pw_gid;
457 yppasswd.newpw.pw_gecos = strdup(pw->pw_gecos);
458 yppasswd.newpw.pw_dir = strdup(pw->pw_dir);
459 yppasswd.newpw.pw_shell = strdup(pw->pw_shell);
460 yppasswd.oldpass = "";
463 /* Get the user's password for authentication purposes. */
465 printf ("Changing NIS information for %s on %s\n",
466 pw->pw_name, master);
468 if (pw->pw_passwd[0] && !suser_override) {
469 password = getpass("Please enter password: ");
470 if (strncmp(crypt(password,pw->pw_passwd),
471 pw->pw_passwd,strlen(pw->pw_passwd))) {
472 warnx("Password incorrect.");
473 pw_error(tempname, 0, 1);
475 yppasswd.oldpass = password; /* XXX */
478 if (suser_override) {
479 /* Talk to server via AF_UNIX socket. */
480 clnt = clnt_create(sockname, MASTER_YPPASSWDPROG,
481 MASTER_YPPASSWDVERS, "unix");
483 warnx("failed to contact rpc.yppasswdd: %s",
484 clnt_spcreateerror(master));
485 pw_error(tempname, 0, 1);
488 /* Create a handle to yppasswdd. */
490 if ((clnt = clnt_create(master, YPPASSWDPROG,
491 YPPASSWDVERS, "udp")) == NULL) {
492 warnx("failed to contact rpc.yppasswdd: %s",
493 clnt_spcreateerror(master));
494 pw_error(tempname, 0, 1);
498 clnt->cl_auth = authunix_create_default();
501 status = yppasswdproc_update_master_1(&master_yppasswd, clnt);
503 status = yppasswdproc_update_1(&yppasswd, clnt);
505 clnt_geterr(clnt, &err);
507 auth_destroy(clnt->cl_auth);
510 /* Call failed: signal the error. */
512 if (err.re_status != RPC_SUCCESS || status == NULL || *status) {
513 warnx("NIS update failed: %s", clnt_sperrno(err.re_status));
514 pw_error(NULL, 0, 1);
520 warnx("NIS information changed on host %s, domain %s",
523 warnx("NIS information changed on host %s", master);