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