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 * $Id: pw_yp.c,v 1.11 1997/07/29 15:45:36 wpaul Exp $
47 #include <sys/types.h>
56 #include <sys/types.h>
58 #include <sys/param.h>
61 #include <rpcsvc/yp.h>
62 struct dom_binding
{};
63 #include <rpcsvc/ypclnt.h>
64 #include <rpcsvc/yppasswd.h>
67 #include "ypxfr_extern.h"
68 #include "yppasswd_private.h"
70 #define PERM_SECURE (S_IRUSR|S_IWUSR)
71 static HASHINFO openinfo
= {
75 2048 * 1024, /* cachesize */
82 int suser_override
= 0;
83 int yp_in_pw_file
= 0;
84 char *yp_domain
= NULL
;
85 char *yp_server
= NULL
;
87 extern char *tempname
;
89 /* Save the local and NIS password information */
90 struct passwd local_password
;
91 struct passwd yp_password
;
93 void copy_yp_pass(p
, x
, m
)
97 register char *t
, *s
= p
;
100 yp_password
.pw_fields
= 0;
102 buf
= (char *)realloc(buf
, m
+ 10);
105 /* Turn all colons into NULLs */
106 while (strchr(s
, ':')) {
107 s
= (strchr(s
, ':') + 1);
112 #define EXPAND(e) e = t; while ((*t++ = *p++));
113 EXPAND(yp_password
.pw_name
);
114 yp_password
.pw_fields
|= _PWF_NAME
;
115 EXPAND(yp_password
.pw_passwd
);
116 yp_password
.pw_fields
|= _PWF_PASSWD
;
117 yp_password
.pw_uid
= atoi(p
);
118 p
+= (strlen(p
) + 1);
119 yp_password
.pw_fields
|= _PWF_UID
;
120 yp_password
.pw_gid
= atoi(p
);
121 p
+= (strlen(p
) + 1);
122 yp_password
.pw_fields
|= _PWF_GID
;
124 EXPAND(yp_password
.pw_class
);
125 yp_password
.pw_fields
|= _PWF_CLASS
;
126 yp_password
.pw_change
= atol(p
);
127 p
+= (strlen(p
) + 1);
128 yp_password
.pw_fields
|= _PWF_CHANGE
;
129 yp_password
.pw_expire
= atol(p
);
130 p
+= (strlen(p
) + 1);
131 yp_password
.pw_fields
|= _PWF_EXPIRE
;
133 EXPAND(yp_password
.pw_gecos
);
134 yp_password
.pw_fields
|= _PWF_GECOS
;
135 EXPAND(yp_password
.pw_dir
);
136 yp_password
.pw_fields
|= _PWF_DIR
;
137 EXPAND(yp_password
.pw_shell
);
138 yp_password
.pw_fields
|= _PWF_SHELL
;
143 void copy_local_pass(p
,m
)
150 buf
= (char *)realloc(buf
, m
+ 10);
154 EXPAND(local_password
.pw_name
);
155 EXPAND(local_password
.pw_passwd
);
156 bcopy(p
, (char *)&local_password
.pw_uid
, sizeof(int));
158 bcopy(p
, (char *)&local_password
.pw_gid
, sizeof(int));
160 bcopy(p
, (char *)&local_password
.pw_change
, sizeof(time_t));
162 EXPAND(local_password
.pw_class
);
163 EXPAND(local_password
.pw_gecos
);
164 EXPAND(local_password
.pw_dir
);
165 EXPAND(local_password
.pw_shell
);
166 bcopy(p
, (char *)&local_password
.pw_expire
, sizeof(time_t));
168 bcopy(p
, (char *)&local_password
.pw_fields
, sizeof local_password
.pw_fields
);
169 p
+= sizeof local_password
.pw_fields
;
175 * It is not mandatory that an NIS master server also be a client.
176 * However, if the NIS master is not configured as a client, then the
177 * domain name will not be set and ypbind will not be running, so we
178 * will be unable to use the ypclnt routines inside libc. We therefore
179 * need our own magic version of yp_match() which we can use in any
182 static int my_yp_match(server
, domain
, map
, key
, keylen
, result
, resultlen
)
187 unsigned long keylen
;
189 unsigned long *resultlen
;
194 static char buf
[YPMAXRECORD
+ 2];
196 bzero((char *)buf
, sizeof(buf
));
198 if ((clnt
= clnt_create(server
, YPPROG
,YPVERS
,"udp")) == NULL
) {
199 warnx("failed to create UDP handle: %s",
200 clnt_spcreateerror(server
));
201 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
) {
211 warnx("%s",clnt_sperror(clnt
,"YPPROC_MATCH failed"));
212 pw_error(tempname
, 0, 1);
217 if (ypval
->stat
!= YP_TRUE
) {
218 int stat
= ypval
->stat
;
219 xdr_free(xdr_ypresp_val
, (char *)ypval
);
220 if (stat
== YP_NOMAP
&& strstr(map
, "master.passwd"))
222 if (stat
== YP_NOKEY
)
224 warnx("ypmatch failed: %s", yperr_string(ypprot_err(stat
)));
225 pw_error(tempname
, 0, 1);
229 strncpy((char *)&buf
, ypval
->val
.valdat_val
, ypval
->val
.valdat_len
);
231 *result
= (char *)&buf
;
232 *resultlen
= ypval
->val
.valdat_len
;
234 xdr_free(xdr_ypresp_val
, (char *)ypval
);
240 * Check if the user we're working with is local or in NIS.
242 int use_yp (user
, uid
, which
)
245 int which
; /* 0 = use username, 1 = use uid */
247 int user_local
= 0, user_yp
= 0, user_exists
= 0;
250 char bf
[UT_NAMESIZE
+ 2];
254 char ubuf
[UT_NAMESIZE
+ 2];
257 snprintf(ubuf
, sizeof(ubuf
), "%lu", uid
);
258 user
= (char *)&ubuf
;
261 /* Grope around for the user in the usual way */
263 if (getpwuid(uid
) != NULL
)
266 if (getpwnam(user
) != NULL
)
270 /* Now grope directly through the user database */
271 if ((dbp
= dbopen(_PATH_SMP_DB
, O_RDONLY
, PERM_SECURE
,
272 DB_HASH
, &openinfo
)) == NULL
) {
273 warn("error opening database: %s.", _PATH_MP_DB
);
274 pw_error(tempname
, 0, 1);
277 /* Is NIS turned on */
278 bf
[0] = _PW_KEYYPENABLED
;
279 key
.data
= (u_char
*)bf
;
281 yp_in_pw_file
= !(dbp
->get
)(dbp
,&key
,&data
,0);
282 if (_yp_check(NULL
) || (yp_domain
&& yp_server
)) {
283 server
= get_yp_master(0);
285 /* Is the user in the NIS passwd map */
286 if (!my_yp_match(server
, yp_domain
, which
? "passwd.byuid" :
287 "passwd.byname", user
, strlen(user
),
288 &result
, &resultlen
)) {
289 user_yp
= user_exists
= 1;
290 *(char *)(result
+ resultlen
) = '\0';
291 copy_yp_pass(result
, 0, resultlen
);
293 /* Is the user in the NIS master.passwd map */
294 if (user_yp
&& !my_yp_match(server
, yp_domain
, which
?
295 "master.passwd.byuid" : "master.passwd.byname",
297 &result
, &resultlen
)) {
298 *(char *)(result
+ resultlen
) = '\0';
299 copy_yp_pass(result
, 1, resultlen
);
303 /* Is the user in the local password database */
305 bf
[0] = which
? _PW_KEYBYUID
: _PW_KEYBYNAME
;
307 bcopy((char *)&uid
, bf
+ 1, sizeof(uid
));
309 bcopy((char *)user
, bf
+ 1, MIN(strlen(user
), UT_NAMESIZE
));
310 key
.data
= (u_char
*)bf
;
311 key
.size
= which
? sizeof(uid
) + 1 : strlen(user
) + 1;
312 if (!(dbp
->get
)(dbp
,&key
,&data
,0)) {
314 copy_local_pass(data
.data
, data
.size
);
319 if (user_local
&& user_yp
&& user_exists
)
320 return(USER_YP_AND_LOCAL
);
321 else if (!user_local
&& user_yp
&& user_exists
)
322 return(USER_YP_ONLY
);
323 else if (user_local
&& !user_yp
&& user_exists
)
324 return(USER_LOCAL_ONLY
);
325 else if (!user_exists
)
326 return(USER_UNKNOWN
);
332 * Find the name of the NIS master server for this domain
333 * and make sure it's running yppasswdd.
335 char *get_yp_master(getserver
)
341 char *sockname
= YP_SOCKNAME
;
344 * Sometimes we are called just to probe for rpc.yppasswdd and
345 * set the suser_override flag. Just return NULL and leave
346 * suser_override at 0 if _use_yp doesn't indicate that NIS is
347 * in use and we weren't called from use_yp() itself.
348 * Without this check, we might try probing and fail with an NIS
349 * error in non-NIS environments.
351 if ((_use_yp
== USER_UNKNOWN
|| _use_yp
== USER_LOCAL_ONLY
) &&
355 /* Get default NIS domain. */
357 if (yp_domain
== NULL
&& (rval
= yp_get_default_domain(&yp_domain
))) {
358 warnx("can't get local NIS domain name: %s",yperr_string(rval
));
359 pw_error(tempname
, 0, 1);
362 /* Get master server of passwd map. */
364 if ((mastername
= ypxfr_get_master(yp_domain
, "passwd.byname",
365 yp_server
, yp_server
? 0 : 1)) == NULL
) {
366 warnx("can't get name of master NIS server");
367 pw_error(tempname
, 0, 1);
373 /* Check if yppasswdd is out there. */
375 if ((rval
= getrpcport(mastername
, YPPASSWDPROG
, YPPASSWDPROC_UPDATE
,
376 IPPROTO_UDP
)) == 0) {
377 warnx("rpc.yppasswdd is not running on the NIS master server");
378 pw_error(tempname
, 0, 1);
382 * Make sure it's on a reserved port.
383 * XXX Might break with yppasswdd servers running on Solaris 2.x.
386 if (rval
>= IPPORT_RESERVED
) {
387 warnx("rpc.yppasswdd server not running on reserved port");
388 pw_error(tempname
, 0, 1);
391 /* See if _we_ are the master server. */
392 if (!force_old
&& !getuid() && (localport
= getrpcport("localhost",
393 YPPASSWDPROG
, YPPASSWDPROC_UPDATE
, IPPROTO_UDP
)) != 0) {
394 if (localport
== rval
&& stat(sockname
, &st
) != -1) {
396 mastername
= "localhost";
400 /* Everything checks out: return the name of the server. */
406 * Ask the user for his NIS password and submit the new information
407 * to yppasswdd. Note that rpc.yppasswdd requires password authentication
408 * and only allows changes to existing records rather than the addition
409 * of new records. (To do actual updates we would need something like
410 * secure RPC and ypupdated, which FreeBSD doesn't have yet.) The FreeBSD
411 * rpc.yppasswdd has some special hooks to allow the superuser update
412 * information without specifying a password, however this only works
413 * for the superuser on the NIS master server.
418 struct yppasswd yppasswd
;
419 struct master_yppasswd master_yppasswd
;
421 char *master
, *password
;
424 char *sockname
= YP_SOCKNAME
;
428 /* Get NIS master server name */
430 master
= get_yp_master(1);
432 /* Populate the yppasswd structure that gets handed to yppasswdd. */
434 if (suser_override
) {
435 master_yppasswd
.newpw
.pw_passwd
= strdup(pw
->pw_passwd
);
436 master_yppasswd
.newpw
.pw_name
= strdup(pw
->pw_name
);
437 master_yppasswd
.newpw
.pw_uid
= pw
->pw_uid
;
438 master_yppasswd
.newpw
.pw_gid
= pw
->pw_gid
;
439 master_yppasswd
.newpw
.pw_expire
= pw
->pw_expire
;
440 master_yppasswd
.newpw
.pw_change
= pw
->pw_change
;
441 master_yppasswd
.newpw
.pw_fields
= pw
->pw_fields
;
442 master_yppasswd
.newpw
.pw_gecos
= strdup(pw
->pw_gecos
);
443 master_yppasswd
.newpw
.pw_dir
= strdup(pw
->pw_dir
);
444 master_yppasswd
.newpw
.pw_shell
= strdup(pw
->pw_shell
);
445 master_yppasswd
.newpw
.pw_class
= pw
->pw_class
!= NULL
?
446 strdup(pw
->pw_class
) : "";
447 master_yppasswd
.oldpass
= ""; /* not really needed */
448 master_yppasswd
.domain
= yp_domain
;
450 yppasswd
.newpw
.pw_passwd
= strdup(pw
->pw_passwd
);
451 yppasswd
.newpw
.pw_name
= strdup(pw
->pw_name
);
452 yppasswd
.newpw
.pw_uid
= pw
->pw_uid
;
453 yppasswd
.newpw
.pw_gid
= pw
->pw_gid
;
454 yppasswd
.newpw
.pw_gecos
= strdup(pw
->pw_gecos
);
455 yppasswd
.newpw
.pw_dir
= strdup(pw
->pw_dir
);
456 yppasswd
.newpw
.pw_shell
= strdup(pw
->pw_shell
);
457 yppasswd
.oldpass
= "";
460 /* Get the user's password for authentication purposes. */
462 printf ("Changing NIS information for %s on %s\n",
463 pw
->pw_name
, master
);
465 if (pw
->pw_passwd
[0] && !suser_override
) {
466 password
= getpass("Please enter password: ");
467 if (strncmp(crypt(password
,pw
->pw_passwd
),
468 pw
->pw_passwd
,strlen(pw
->pw_passwd
))) {
469 warnx("Password incorrect.");
470 pw_error(tempname
, 0, 1);
472 yppasswd
.oldpass
= password
; /* XXX */
475 if (suser_override
) {
476 /* Talk to server via AF_UNIX socket. */
477 clnt
= clnt_create(sockname
, MASTER_YPPASSWDPROG
,
478 MASTER_YPPASSWDVERS
, "unix");
480 warnx("failed to contact rpc.yppasswdd: %s",
481 clnt_spcreateerror(master
));
482 pw_error(tempname
, 0, 1);
485 /* Create a handle to yppasswdd. */
487 if ((clnt
= clnt_create(master
, YPPASSWDPROG
,
488 YPPASSWDVERS
, "udp")) == NULL
) {
489 warnx("failed to contact rpc.yppasswdd: %s",
490 clnt_spcreateerror(master
));
491 pw_error(tempname
, 0, 1);
495 clnt
->cl_auth
= authunix_create_default();
498 status
= yppasswdproc_update_master_1(&master_yppasswd
, clnt
);
500 status
= yppasswdproc_update_1(&yppasswd
, clnt
);
502 clnt_geterr(clnt
, &err
);
504 auth_destroy(clnt
->cl_auth
);
507 /* Call failed: signal the error. */
509 if (err
.re_status
!= RPC_SUCCESS
|| status
== NULL
|| *status
) {
510 warnx("NIS update failed: %s", clnt_sperrno(err
.re_status
));
511 pw_error(NULL
, 0, 1);
517 warnx("NIS information changed on host %s, domain %s",
520 warnx("NIS information changed on host %s", master
);