]> git.cameronkatri.com Git - pw-darwin.git/blob - chpass/pw_yp.c
df8a08256991511c6e64ed25cea57e18ba2197a1
[pw-darwin.git] / chpass / pw_yp.c
1 /*
2 * Copyright (c) 1995
3 * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
19 *
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
30 * SUCH DAMAGE.
31 *
32 * NIS interface routines for chpass
33 *
34 * Written by Bill Paul <wpaul@ctr.columbia.edu>
35 * Center for Telecommunications Research
36 * Columbia University, New York City
37 */
38
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
41
42 #ifdef YP
43 #include <sys/param.h>
44 #include <sys/stat.h>
45 #include <sys/stat.h>
46 #include <sys/types.h>
47
48 #include <rpc/rpc.h>
49 #include <rpcsvc/yp.h>
50 #include <rpcsvc/ypclnt.h>
51 #include <rpcsvc/yppasswd.h>
52
53 #include <db.h>
54 #include <err.h>
55 #include <errno.h>
56 #include <fcntl.h>
57 #include <limits.h>
58 #include <netdb.h>
59 #include <pw_util.h>
60 #include <pwd.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <time.h>
65 #include <unistd.h>
66 #include <utmp.h>
67
68 #include "pw_yp.h"
69 #include "ypxfr_extern.h"
70 #include "yppasswd_private.h"
71
72 #define PERM_SECURE (S_IRUSR|S_IWUSR)
73 static HASHINFO openinfo = {
74 4096, /* bsize */
75 32, /* ffactor */
76 256, /* nelem */
77 2048 * 1024, /* cachesize */
78 NULL, /* hash */
79 0, /* lorder */
80 };
81
82 static char passwdbyname[] = "passwd.byname";
83 static char localhost[] = "localhost";
84
85 int force_old = 0;
86 int _use_yp = 0;
87 int suser_override = 0;
88 int yp_in_pw_file = 0;
89 char *yp_domain = NULL;
90 char *yp_server = NULL;
91
92 extern char *tempname;
93
94 /* Save the local and NIS password information */
95 struct passwd local_password;
96 struct passwd yp_password;
97
98 void
99 copy_yp_pass(char *p, int x, int m)
100 {
101 register char *t, *s = p;
102 static char *buf;
103
104 yp_password.pw_fields = 0;
105
106 buf = (char *)realloc(buf, m + 10);
107 bzero(buf, m + 10);
108
109 /* Turn all colons into NULLs */
110 while (strchr(s, ':')) {
111 s = (strchr(s, ':') + 1);
112 *(s - 1)= '\0';
113 }
114
115 t = buf;
116 #define EXPAND(e) e = t; while ((*t++ = *p++));
117 EXPAND(yp_password.pw_name);
118 yp_password.pw_fields |= _PWF_NAME;
119 EXPAND(yp_password.pw_passwd);
120 yp_password.pw_fields |= _PWF_PASSWD;
121 yp_password.pw_uid = atoi(p);
122 p += (strlen(p) + 1);
123 yp_password.pw_fields |= _PWF_UID;
124 yp_password.pw_gid = atoi(p);
125 p += (strlen(p) + 1);
126 yp_password.pw_fields |= _PWF_GID;
127 if (x) {
128 EXPAND(yp_password.pw_class);
129 yp_password.pw_fields |= _PWF_CLASS;
130 yp_password.pw_change = atol(p);
131 p += (strlen(p) + 1);
132 yp_password.pw_fields |= _PWF_CHANGE;
133 yp_password.pw_expire = atol(p);
134 p += (strlen(p) + 1);
135 yp_password.pw_fields |= _PWF_EXPIRE;
136 }
137 EXPAND(yp_password.pw_gecos);
138 yp_password.pw_fields |= _PWF_GECOS;
139 EXPAND(yp_password.pw_dir);
140 yp_password.pw_fields |= _PWF_DIR;
141 EXPAND(yp_password.pw_shell);
142 yp_password.pw_fields |= _PWF_SHELL;
143
144 return;
145 }
146
147 static void
148 copy_local_pass(char *p, int m)
149 {
150 register char *t;
151 static char *buf;
152
153 buf = (char *)realloc(buf, m + 10);
154 bzero(buf, m + 10);
155
156 t = buf;
157 EXPAND(local_password.pw_name);
158 EXPAND(local_password.pw_passwd);
159 bcopy(p, (char *)&local_password.pw_uid, sizeof(int));
160 p += sizeof(int);
161 bcopy(p, (char *)&local_password.pw_gid, sizeof(int));
162 p += sizeof(int);
163 bcopy(p, (char *)&local_password.pw_change, sizeof(time_t));
164 p += sizeof(time_t);
165 EXPAND(local_password.pw_class);
166 EXPAND(local_password.pw_gecos);
167 EXPAND(local_password.pw_dir);
168 EXPAND(local_password.pw_shell);
169 bcopy(p, (char *)&local_password.pw_expire, sizeof(time_t));
170 p += sizeof(time_t);
171 bcopy(p, (char *)&local_password.pw_fields, sizeof local_password.pw_fields);
172 p += sizeof local_password.pw_fields;
173
174 return;
175 }
176
177 /*
178 * It is not mandatory that an NIS master server also be a client.
179 * However, if the NIS master is not configured as a client, then the
180 * domain name will not be set and ypbind will not be running, so we
181 * will be unable to use the ypclnt routines inside libc. We therefore
182 * need our own magic version of yp_match() which we can use in any
183 * environment.
184 */
185 static int
186 my_yp_match(char *server, char *domain, const char *map, char *key,
187 unsigned long keylen, char **result, unsigned long *resultlen)
188 {
189 ypreq_key ypkey;
190 ypresp_val *ypval;
191 CLIENT *clnt;
192 static char buf[YPMAXRECORD + 2];
193
194 bzero((char *)buf, sizeof(buf));
195
196 /*
197 * Don't make this a fatal error. The inability to contact
198 * a server is, for our purposes, equivalent to not finding
199 * the record we were looking for. Letting use_yp() know
200 * that the lookup failed is sufficient.
201 */
202 if ((clnt = clnt_create(server, YPPROG,YPVERS,"udp")) == NULL)
203 return(1);
204
205 ypkey.domain = domain;
206 ypkey.map = strdup(map);
207 ypkey.key.keydat_len = keylen;
208 ypkey.key.keydat_val = key;
209
210 if ((ypval = ypproc_match_2(&ypkey, clnt)) == NULL) {
211 clnt_destroy(clnt);
212 return(1);
213 }
214
215 clnt_destroy(clnt);
216
217 if (ypval->stat != YP_TRUE) {
218 xdr_free(xdr_ypresp_val, (char *)ypval);
219 return(1);
220 }
221
222
223 strncpy((char *)&buf, ypval->val.valdat_val, ypval->val.valdat_len);
224
225 *result = (char *)&buf;
226 *resultlen = ypval->val.valdat_len;
227
228 xdr_free(xdr_ypresp_val, (char *)ypval);
229
230 return(0);
231 }
232
233 /*
234 * Check if the user we're working with is local or in NIS.
235 */
236 int
237 use_yp(char *user, uid_t uid, int which /* 0 = use username, 1 = use uid */)
238 {
239 int user_local = 0, user_yp = 0, user_exists = 0;
240 DB *dbp;
241 DBT key,data;
242 char bf[UT_NAMESIZE + 2];
243 char *result;
244 char *server;
245 long resultlen;
246 char ubuf[UT_NAMESIZE + 2];
247
248 if (which) {
249 snprintf(ubuf, sizeof(ubuf), "%lu", (unsigned long)uid);
250 user = (char *)&ubuf;
251 }
252
253 /* Grope around for the user in the usual way */
254 if (which) {
255 if (getpwuid(uid) != NULL)
256 user_exists = 1;
257 } else {
258 if (getpwnam(user) != NULL)
259 user_exists = 1;
260 }
261
262 /* Now grope directly through the user database */
263 if ((dbp = dbopen(_PATH_SMP_DB, O_RDONLY, PERM_SECURE,
264 DB_HASH, &openinfo)) == NULL) {
265 warn("error opening database: %s.", _PATH_MP_DB);
266 pw_error(tempname, 0, 1);
267 }
268
269 /* Is NIS turned on */
270 bf[0] = _PW_KEYYPENABLED;
271 key.data = (u_char *)bf;
272 key.size = 1;
273 yp_in_pw_file = !(dbp->get)(dbp,&key,&data,0);
274 if (_yp_check(NULL) || (yp_domain && yp_server)) {
275 server = get_yp_master(0);
276
277 /* Is the user in the NIS passwd map */
278 if (!my_yp_match(server, yp_domain, which ? "passwd.byuid" :
279 "passwd.byname", user, strlen(user),
280 &result, &resultlen)) {
281 user_yp = user_exists = 1;
282 *(char *)(result + resultlen) = '\0';
283 copy_yp_pass(result, 0, resultlen);
284 }
285 /* Is the user in the NIS master.passwd map */
286 if (user_yp && !my_yp_match(server, yp_domain, which ?
287 "master.passwd.byuid" : "master.passwd.byname",
288 user, strlen(user),
289 &result, &resultlen)) {
290 *(char *)(result + resultlen) = '\0';
291 copy_yp_pass(result, 1, resultlen);
292 }
293 }
294
295 /* Is the user in the local password database */
296
297 bf[0] = which ? _PW_KEYBYUID : _PW_KEYBYNAME;
298 if (which)
299 bcopy((char *)&uid, bf + 1, sizeof(uid));
300 else
301 bcopy((char *)user, bf + 1, MIN(strlen(user), UT_NAMESIZE));
302 key.data = (u_char *)bf;
303 key.size = which ? sizeof(uid) + 1 : strlen(user) + 1;
304 if (!(dbp->get)(dbp,&key,&data,0)) {
305 user_local = 1;
306 copy_local_pass(data.data, data.size);
307 }
308
309 (dbp->close)(dbp);
310
311 if (user_local && user_yp && user_exists)
312 return(USER_YP_AND_LOCAL);
313 else if (!user_local && user_yp && user_exists)
314 return(USER_YP_ONLY);
315 else if (user_local && !user_yp && user_exists)
316 return(USER_LOCAL_ONLY);
317 else if (!user_exists)
318 return(USER_UNKNOWN);
319
320 return(-1);
321 }
322
323 /*
324 * Find the name of the NIS master server for this domain
325 * and make sure it's running yppasswdd.
326 */
327 char *
328 get_yp_master(int getserver)
329 {
330 char *mastername;
331 int rval, localport;
332
333 /*
334 * Sometimes we are called just to probe for rpc.yppasswdd and
335 * set the suser_override flag. Just return NULL and leave
336 * suser_override at 0 if _use_yp doesn't indicate that NIS is
337 * in use and we weren't called from use_yp() itself.
338 * Without this check, we might try probing and fail with an NIS
339 * error in non-NIS environments.
340 */
341 if ((_use_yp == USER_UNKNOWN || _use_yp == USER_LOCAL_ONLY) &&
342 getserver)
343 return(NULL);
344
345 /* Get default NIS domain. */
346
347 if (yp_domain == NULL && (rval = yp_get_default_domain(&yp_domain))) {
348 warnx("can't get local NIS domain name: %s",yperr_string(rval));
349 pw_error(tempname, 0, 1);
350 }
351
352 /* Get master server of passwd map. */
353
354 if ((mastername = ypxfr_get_master(yp_domain, passwdbyname,
355 yp_server, yp_server ? 0 : 1)) == NULL) {
356 warnx("can't get name of master NIS server");
357 pw_error(tempname, 0, 1);
358 }
359
360 if (!getserver)
361 return(mastername);
362
363 /* Check if yppasswdd is out there. */
364
365 if ((rval = getrpcport(mastername, YPPASSWDPROG, YPPASSWDPROC_UPDATE,
366 IPPROTO_UDP)) == 0) {
367 warnx("rpc.yppasswdd is not running on the NIS master server");
368 pw_error(tempname, 0, 1);
369 }
370
371 /*
372 * Make sure it's on a reserved port.
373 * XXX Might break with yppasswdd servers running on Solaris 2.x.
374 */
375
376 if (rval >= IPPORT_RESERVED) {
377 warnx("rpc.yppasswdd server not running on reserved port");
378 pw_error(tempname, 0, 1);
379 }
380
381 /* See if _we_ are the master server. */
382 if (!force_old && !getuid() && (localport = getrpcport(localhost,
383 YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP)) != 0) {
384 if (localport == rval) {
385 suser_override = 1;
386 mastername = localhost;
387 }
388 }
389
390 /* Everything checks out: return the name of the server. */
391
392 return (mastername);
393 }
394
395 /*
396 * Ask the user for his NIS password and submit the new information
397 * to yppasswdd. Note that rpc.yppasswdd requires password authentication
398 * and only allows changes to existing records rather than the addition
399 * of new records. (To do actual updates we would need something like
400 * secure RPC and ypupdated, which FreeBSD doesn't have yet.) The FreeBSD
401 * rpc.yppasswdd has some special hooks to allow the superuser update
402 * information without specifying a password, however this only works
403 * for the superuser on the NIS master server.
404 */
405 void
406 yp_submit(struct passwd *pw)
407 {
408 struct yppasswd yppwd;
409 struct master_yppasswd master_yppwd;
410 struct netconfig *nconf;
411 void *localhandle;
412 CLIENT *clnt;
413 char *master, *password;
414 int *status = NULL;
415 struct rpc_err lerr;
416
417 nconf = NULL;
418 _use_yp = 1;
419
420 /* Get NIS master server name */
421
422 master = get_yp_master(1);
423
424 /* Populate the yppasswd structure that gets handed to yppasswdd. */
425
426 if (suser_override) {
427 master_yppwd.newpw.pw_passwd = strdup(pw->pw_passwd);
428 master_yppwd.newpw.pw_name = strdup(pw->pw_name);
429 master_yppwd.newpw.pw_uid = pw->pw_uid;
430 master_yppwd.newpw.pw_gid = pw->pw_gid;
431 master_yppwd.newpw.pw_expire = pw->pw_expire;
432 master_yppwd.newpw.pw_change = pw->pw_change;
433 master_yppwd.newpw.pw_fields = pw->pw_fields;
434 master_yppwd.newpw.pw_gecos = strdup(pw->pw_gecos);
435 master_yppwd.newpw.pw_dir = strdup(pw->pw_dir);
436 master_yppwd.newpw.pw_shell = strdup(pw->pw_shell);
437 master_yppwd.newpw.pw_class = pw->pw_class != NULL
438 ? strdup(pw->pw_class)
439 : strdup("");
440 master_yppwd.oldpass = strdup(""); /* not really needed */
441 master_yppwd.domain = yp_domain;
442 } else {
443 yppwd.newpw.pw_passwd = strdup(pw->pw_passwd);
444 yppwd.newpw.pw_name = strdup(pw->pw_name);
445 yppwd.newpw.pw_uid = pw->pw_uid;
446 yppwd.newpw.pw_gid = pw->pw_gid;
447 yppwd.newpw.pw_gecos = strdup(pw->pw_gecos);
448 yppwd.newpw.pw_dir = strdup(pw->pw_dir);
449 yppwd.newpw.pw_shell = strdup(pw->pw_shell);
450 yppwd.oldpass = strdup("");
451 }
452
453 /* Get the user's password for authentication purposes. */
454
455 printf ("Changing NIS information for %s on %s\n",
456 pw->pw_name, master);
457
458 if (pw->pw_passwd[0] && !suser_override) {
459 password = getpass("Please enter password: ");
460 if (strncmp(crypt(password,pw->pw_passwd),
461 pw->pw_passwd,strlen(pw->pw_passwd))) {
462 warnx("Password incorrect.");
463 pw_error(tempname, 0, 1);
464 }
465 yppwd.oldpass = password; /* XXX */
466 }
467
468 if (suser_override) {
469 /* Talk to server via AF_UNIX socket. */
470 localhandle = setnetconfig();
471 while ((nconf = getnetconfig(localhandle)) != NULL) {
472 if (nconf->nc_protofmly != NULL &&
473 strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0)
474 break;
475 }
476 if (nconf == NULL) {
477 warnx("getnetconfig: %s", nc_sperror());
478 pw_error(tempname, 0, 1);
479 }
480
481 clnt = clnt_tp_create(NULL, MASTER_YPPASSWDPROG,
482 MASTER_YPPASSWDVERS, nconf);
483 if (clnt == NULL) {
484 warnx("failed to contact rpc.yppasswdd: %s",
485 clnt_spcreateerror(master));
486 endnetconfig(localhandle);
487 pw_error(tempname, 0, 1);
488 }
489 endnetconfig(localhandle);
490 } else {
491 /* Create a handle to yppasswdd. */
492
493 if ((clnt = clnt_create(master, YPPASSWDPROG,
494 YPPASSWDVERS, "udp")) == NULL) {
495 warnx("failed to contact rpc.yppasswdd: %s",
496 clnt_spcreateerror(master));
497 pw_error(tempname, 0, 1);
498 }
499 }
500
501 clnt->cl_auth = authunix_create_default();
502
503 if (suser_override)
504 status = yppasswdproc_update_master_1(&master_yppwd, clnt);
505 else
506 status = yppasswdproc_update_1(&yppwd, clnt);
507
508 clnt_geterr(clnt, &lerr);
509
510 auth_destroy(clnt->cl_auth);
511 clnt_destroy(clnt);
512
513 /* Call failed: signal the error. */
514
515 if (lerr.re_status != RPC_SUCCESS || status == NULL || *status) {
516 warnx("NIS update failed: %s", clnt_sperrno(lerr.re_status));
517 pw_error(NULL, 0, 1);
518 }
519
520 /* Success. */
521
522 if (suser_override)
523 warnx("NIS information changed on host %s, domain %s",
524 master, yp_domain);
525 else
526 warnx("NIS information changed on host %s", master);
527
528 return;
529 }
530 #endif /* YP */