xref: /illumos-gate/usr/src/lib/passwdutil/nis_attr.c (revision 068cf9dc70b00292d5e5ff1ab17f1b046215d6f6)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  * Copyright 2024 OmniOS Community Edition (OmniOSce) Association.
25  */
26 
27 #include <stdio.h>
28 #include <errno.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/errno.h>
32 #include <pwd.h>
33 #include <unistd.h>
34 #include <syslog.h>
35 
36 #include <netdb.h>
37 
38 #include <rpc/rpc.h>
39 #include <rpcsvc/yppasswd.h>
40 #include <rpcsvc/ypclnt.h>
41 #include <rpcsvc/yp_prot.h>
42 
43 #include "passwdutil.h"
44 
45 int nis_getattr(const char *name, attrlist *item, pwu_repository_t *rep);
46 int nis_getpwnam(const char *name, attrlist *items, pwu_repository_t *rep,
47     void **buf);
48 int nis_update(attrlist *items, pwu_repository_t *rep, void *buf);
49 int nis_putpwnam(const char *name, const char *oldpw, pwu_repository_t *rep,
50     void *buf);
51 int nis_user_to_authenticate(const char *user, pwu_repository_t *rep,
52 	char **auth_user, int *privileged);
53 
54 /*
55  * nis function pointer table, used by passwdutil_init to initialize
56  * the global Repository-OPerations table "rops"
57  */
58 struct repops nis_repops = {
59 	NULL,	/* checkhistory */
60 	nis_getattr,
61 	nis_getpwnam,
62 	nis_update,
63 	nis_putpwnam,
64 	nis_user_to_authenticate,
65 	NULL,	/* lock */
66 	NULL	/* unlock */
67 };
68 
69 /*
70  * structure used to keep state between get/update/put calls
71  */
72 typedef struct {
73 	char *domain;
74 	char *master;
75 	char *scratch;
76 	int scratchlen;
77 	char *c2scratch;
78 	int c2scratchlen;
79 	struct passwd *pwd;
80 } nisbuf_t;
81 
82 /*
83  * Are we a 'privileged' process? Yes if we are running on the
84  * NIS server AND we are root...
85  */
86 int
87 nis_privileged(nisbuf_t *nisbuf)
88 {
89 	char thishost[MAXHOSTNAMELEN];
90 	if (gethostname(thishost, sizeof (thishost)) == -1) {
91 		syslog(LOG_ERR, "passwdutil.so: Can't get hostname");
92 		return (0);
93 	}
94 
95 	if (strcmp(nisbuf->master, thishost) != 0)
96 		return (0);
97 
98 	/* We're running on the NIS server. */
99 	return (getuid() == 0);
100 }
101 
102 /*
103  * nis_to_pwd()
104  *
105  * convert password-entry-line to "struct passwd"
106  */
107 void
108 nis_to_pwd(char *nis, struct passwd *pwd)
109 {
110 	pwd->pw_name = strsep(&nis, ":");
111 	pwd->pw_passwd = strsep(&nis, ":");
112 	pwd->pw_uid = atoi(strsep(&nis, ":"));
113 	pwd->pw_gid = atoi(strsep(&nis, ":"));
114 	pwd->pw_gecos = strsep(&nis, ":");
115 	pwd->pw_dir = strsep(&nis, ":");
116 	pwd->pw_shell = nis;
117 	if (pwd->pw_shell[0])
118 		pwd->pw_shell[strlen(pwd->pw_shell)-1] = '\0';
119 }
120 
121 /*
122  * nis_user_to_authenticate(name, rep, auth_user, privileged)
123  *
124  */
125 /*ARGSUSED*/
126 int
127 nis_user_to_authenticate(const char *user, pwu_repository_t *rep,
128 	char **auth_user, int *privileged)
129 {
130 	nisbuf_t *buf = NULL;
131 	int res;
132 	attrlist attr_tmp[1];
133 	uid_t uid;
134 
135 	/*
136 	 * special NIS case: don't bother to get "root" from NIS
137 	 */
138 	if (strcmp(user, "root") == 0)
139 		return (PWU_NOT_FOUND);
140 
141 	attr_tmp[0].type = ATTR_UID;
142 	attr_tmp[0].next = NULL;
143 
144 	res = nis_getpwnam(user, &attr_tmp[0], rep, (void **)&buf);
145 
146 	if (res != PWU_SUCCESS)
147 		return (res);
148 
149 	if (nis_privileged(buf)) {
150 		*privileged = 1;
151 		*auth_user = NULL;
152 		res = PWU_SUCCESS;
153 	} else {
154 		uid = getuid();
155 
156 		*privileged = (uid == (uid_t)0);
157 
158 		/* root, or user herself can change attributes */
159 		if (uid == 0 || uid == buf->pwd->pw_uid) {
160 			*auth_user = strdup(user);
161 			res = PWU_SUCCESS;
162 		} else {
163 			res = PWU_DENIED;
164 		}
165 	}
166 
167 	/*
168 	 * Do not release buf->domain.
169 	 * It's been set by yp_get_default_domain()
170 	 * and must not be freed.
171 	 * See man page yp_get_default_domain(3NSL)
172 	 * for details.
173 	 */
174 	if (buf->master)
175 		free(buf->master);
176 	if (buf->scratch)
177 		free(buf->scratch);
178 	if (buf->c2scratch)
179 		free(buf->c2scratch);
180 	free(buf->pwd);
181 	free(buf);
182 
183 	return (res);
184 }
185 
186 
187 /*
188  * nis_getattr(name, items, rep)
189  *
190  * get account attributes specified in 'items'
191  */
192 int
193 nis_getattr(const char *name, attrlist *items, pwu_repository_t *rep)
194 {
195 	nisbuf_t *nisbuf = NULL;
196 	struct passwd *pw;
197 	attrlist *w;
198 	int res;
199 
200 	res = nis_getpwnam(name, items, rep, (void **)&nisbuf);
201 	if (res != PWU_SUCCESS)
202 		return (res);
203 
204 	pw = nisbuf->pwd;
205 
206 	for (w = items; w != NULL; w = w->next) {
207 		switch (w->type) {
208 		case ATTR_NAME:
209 			if ((w->data.val_s = strdup(pw->pw_name)) == NULL)
210 				res = PWU_NOMEM;
211 			break;
212 		case ATTR_COMMENT:
213 			if ((w->data.val_s = strdup(pw->pw_comment)) == NULL)
214 				res = PWU_NOMEM;
215 			break;
216 		case ATTR_GECOS:
217 			if ((w->data.val_s = strdup(pw->pw_gecos)) == NULL)
218 				res = PWU_NOMEM;
219 			break;
220 		case ATTR_HOMEDIR:
221 			if ((w->data.val_s = strdup(pw->pw_dir)) == NULL)
222 				res = PWU_NOMEM;
223 			break;
224 		case ATTR_SHELL:
225 			if ((w->data.val_s = strdup(pw->pw_shell)) == NULL)
226 				res = PWU_NOMEM;
227 			break;
228 		case ATTR_PASSWD:
229 		case ATTR_PASSWD_SERVER_POLICY:
230 			if ((w->data.val_s = strdup(pw->pw_passwd)) == NULL)
231 				res = PWU_NOMEM;
232 			break;
233 		case ATTR_REP_NAME:
234 			if ((w->data.val_s = strdup("nis")) == NULL)
235 				res = PWU_NOMEM;
236 			break;
237 
238 		/* integer values */
239 		case ATTR_UID:
240 			w->data.val_i = nisbuf->pwd->pw_uid;
241 			break;
242 		case ATTR_GID:
243 			w->data.val_i = nisbuf->pwd->pw_gid;
244 			break;
245 		case ATTR_LSTCHG:
246 		case ATTR_MIN:
247 		case ATTR_MAX:
248 		case ATTR_WARN:
249 		case ATTR_INACT:
250 		case ATTR_EXPIRE:
251 		case ATTR_FLAG:
252 		case ATTR_AGE:
253 			w->data.val_i = -1;	/* not used for NIS */
254 			break;
255 		default:
256 			break;
257 		}
258 	}
259 
260 	/*
261 	 * Do not release nisbuf->domain.
262 	 * It's been set by yp_get_default_domain()
263 	 * and must not be freed.
264 	 * See man page yp_get_default_domain(3NSL)
265 	 * for details.
266 	 */
267 	if (nisbuf->master)
268 		free(nisbuf->master);
269 	if (nisbuf->scratch)
270 		free(nisbuf->scratch);
271 	if (nisbuf->c2scratch)
272 		free(nisbuf->c2scratch);
273 	free(nisbuf->pwd);
274 	free(nisbuf);
275 
276 	return (res);
277 }
278 
279 /*
280  * nis_getpwnam(name, items, rep)
281  *
282  * Get the account information of user 'name'
283  */
284 /*ARGSUSED*/
285 int
286 nis_getpwnam(const char *name, attrlist *items, pwu_repository_t *rep,
287     void **buf)
288 {
289 	nisbuf_t *nisbuf;
290 	int nisresult;
291 	char *ncname;
292 
293 	nisbuf = calloc(sizeof (*nisbuf), 1);
294 	if (nisbuf == NULL)
295 		return (PWU_NOMEM);
296 
297 	nisbuf->pwd = malloc(sizeof (struct passwd));
298 	if (nisbuf->pwd == NULL) {
299 		free(nisbuf);
300 		return (PWU_NOMEM);
301 	}
302 
303 	/*
304 	 * Do not release nisbuf->domain.
305 	 * It is going to be set by yp_get_default_domain()
306 	 * and must not be freed.
307 	 * See man page yp_get_default_domain(3NSL)
308 	 * for details.
309 	 */
310 	if (yp_get_default_domain(&nisbuf->domain) != 0) {
311 		syslog(LOG_ERR, "passwdutil.so: can't get domain");
312 		free(nisbuf->pwd);
313 		free(nisbuf);
314 		return (PWU_SERVER_ERROR);
315 	}
316 
317 	if (yp_master(nisbuf->domain, "passwd.byname", &nisbuf->master) != 0) {
318 		syslog(LOG_ERR,
319 		    "passwdutil.so: can't get master for passwd map");
320 		free(nisbuf->master);
321 		free(nisbuf->pwd);
322 		free(nisbuf);
323 		return (PWU_SERVER_ERROR);
324 	}
325 
326 	ncname = strdup(name);
327 	if (ncname == NULL) {
328 		free(nisbuf->master);
329 		free(nisbuf->pwd);
330 		free(nisbuf);
331 		return (PWU_NOMEM);
332 	}
333 
334 	nisresult = yp_match(nisbuf->domain, "passwd.byname", ncname,
335 	    strlen(ncname), &(nisbuf->scratch),
336 	    &(nisbuf->scratchlen));
337 	free(ncname);
338 	if (nisresult != 0) {
339 		free(nisbuf->pwd);
340 		free(nisbuf->scratch);
341 		free(nisbuf->master);
342 		free(nisbuf);
343 		return (PWU_NOT_FOUND);
344 	}
345 
346 	nis_to_pwd(nisbuf->scratch, nisbuf->pwd);
347 
348 	/*
349 	 * check for the C2 security flag "##" in the passwd field.
350 	 * If the first 2 chars in the passwd field is "##", get
351 	 * the user's passwd from passwd.adjunct.byname map.
352 	 * The lookup to this passwd.adjunct.byname map will only
353 	 * succeed if the caller's uid is 0 because only root user
354 	 * can use privilege port.
355 	 */
356 	if (nisbuf->pwd->pw_passwd[0] == '#' &&
357 	    nisbuf->pwd->pw_passwd[1] == '#') {
358 		char *key = &nisbuf->pwd->pw_passwd[2];
359 		int keylen;
360 		char *p;
361 
362 		keylen = strlen(key);
363 
364 		nisresult = yp_match(nisbuf->domain, "passwd.adjunct.byname",
365 		    key, keylen, &(nisbuf->c2scratch),
366 		    &(nisbuf->c2scratchlen));
367 
368 		if (nisresult == 0 && nisbuf->c2scratch != NULL) {
369 			/* Skip username (first field), and pick up password */
370 			p = nisbuf->c2scratch;
371 			(void) strsep(&p, ":");
372 			nisbuf->pwd->pw_passwd = strsep(&p, ":");
373 		}
374 	}
375 
376 	*buf = (void *)nisbuf;
377 
378 	return (PWU_SUCCESS);
379 }
380 
381 /*
382  * nis_update(items, rep, buf)
383  *
384  * update the information in "buf" with the attribute/values
385  * specified in "items".
386  */
387 /*ARGSUSED*/
388 int
389 nis_update(attrlist *items, pwu_repository_t *rep, void *buf)
390 {
391 	attrlist *p;
392 	nisbuf_t *nisbuf = (nisbuf_t *)buf;
393 	char *salt;
394 
395 	for (p = items; p != NULL; p = p->next) {
396 		switch (p->type) {
397 		case ATTR_NAME:
398 			break;
399 		/*
400 		 * Nothing special needs to be done for
401 		 * server policy
402 		 */
403 		case ATTR_PASSWD:
404 		case ATTR_PASSWD_SERVER_POLICY:
405 			salt = crypt_gensalt(
406 			    nisbuf->pwd->pw_passwd, nisbuf->pwd);
407 
408 			if (salt == NULL) {
409 				if (errno == ENOMEM)
410 					return (PWU_NOMEM);
411 				else {
412 					/* algorithm problem? */
413 					syslog(LOG_AUTH | LOG_ALERT,
414 					    "passwdutil: crypt_gensalt "
415 					    "%m");
416 					return (PWU_UPDATE_FAILED);
417 				}
418 			}
419 			nisbuf->pwd->pw_passwd = crypt(p->data.val_s, salt);
420 			free(salt);
421 			break;
422 		case ATTR_UID:
423 			nisbuf->pwd->pw_uid = (uid_t)p->data.val_i;
424 			break;
425 		case ATTR_GID:
426 			nisbuf->pwd->pw_gid = (gid_t)p->data.val_i;
427 			break;
428 		case ATTR_AGE:
429 			nisbuf->pwd->pw_age = p->data.val_s;
430 			break;
431 		case ATTR_COMMENT:
432 			nisbuf->pwd->pw_comment = p->data.val_s;
433 			break;
434 		case ATTR_GECOS:
435 			nisbuf->pwd->pw_gecos = p->data.val_s;
436 			break;
437 		case ATTR_HOMEDIR:
438 			nisbuf->pwd->pw_dir = p->data.val_s;
439 			break;
440 		case ATTR_SHELL:
441 			nisbuf->pwd->pw_shell = p->data.val_s;
442 			break;
443 		case ATTR_LSTCHG:
444 		case ATTR_MIN:
445 		case ATTR_MAX:
446 		case ATTR_WARN:
447 		case ATTR_INACT:
448 		case ATTR_EXPIRE:
449 		case ATTR_FLAG:
450 		default:
451 			break;
452 		}
453 	}
454 	return (PWU_SUCCESS);
455 }
456 
457 /*
458  * nis_putpwnam(name, oldpw, rep, buf)
459  *
460  * Update the NIS server. The passwd structure in buf will be sent to
461  * the server for user "name" authenticating with password "oldpw".
462  */
463 /*ARGSUSED*/
464 int
465 nis_putpwnam(const char *name, const char *oldpw, pwu_repository_t *rep,
466 	void *buf)
467 {
468 	nisbuf_t *nisbuf = (nisbuf_t *)buf;
469 	struct yppasswd yppasswd;
470 	struct netconfig *nconf;
471 	int ok;
472 	enum clnt_stat ans;
473 	CLIENT *client;
474 	struct timeval timeout;
475 	char *oldpass;
476 
477 	if (strcmp(name, "root") == 0)
478 		return (PWU_NOT_FOUND);
479 
480 	oldpass = yppasswd.oldpass = strdup(oldpw != NULL ? oldpw : "");
481 	if (oldpass == NULL)
482 		return (PWU_NOMEM);
483 	yppasswd.newpw = *nisbuf->pwd;
484 
485 	/*
486 	 * If we are privileged, we create a ticlts connection to the
487 	 * NIS server so that it can check our credentials
488 	 */
489 	if (nis_privileged(nisbuf)) {
490 		nconf = getnetconfigent("ticlts");
491 		if (!nconf) {
492 			free(oldpass);
493 			syslog(LOG_ERR,
494 			    "passwdutil.so: Couldn't get netconfig entry");
495 			return (PWU_SYSTEM_ERROR);
496 		}
497 		client = clnt_tp_create(nisbuf->master, YPPASSWDPROG,
498 		    YPPASSWDVERS, nconf);
499 		freenetconfigent(nconf);
500 	} else {
501 		/* Try IPv6 first */
502 		client = clnt_create(nisbuf->master, YPPASSWDPROG,
503 		    YPPASSWDVERS, "udp6");
504 		if (client == NULL)
505 			client = clnt_create(nisbuf->master, YPPASSWDPROG,
506 			    YPPASSWDVERS, "udp");
507 	}
508 
509 	if (client == NULL) {
510 		free(oldpass);
511 		syslog(LOG_ERR,
512 		    "passwdutil.so: couldn't create client to YP master");
513 		return (PWU_SERVER_ERROR);
514 	}
515 
516 	timeout.tv_usec = 0;
517 	timeout.tv_sec = 55;	/* ndp uses 55 seconds */
518 
519 	ans = CLNT_CALL(client, YPPASSWDPROC_UPDATE, xdr_yppasswd,
520 	    (char *)&yppasswd, xdr_int, (char *)&ok, timeout);
521 
522 	free(oldpass);
523 	free(nisbuf->pwd);
524 	free(nisbuf->master);
525 	free(nisbuf->scratch);
526 	free(nisbuf->c2scratch);
527 
528 	(void) clnt_destroy(client);
529 
530 	if (ans != RPC_SUCCESS) {
531 		return (PWU_UPDATE_FAILED);
532 	}
533 
534 	/* These errors are obtained from the yppasswdd.c code */
535 	switch (ok) {
536 		case 2: return (PWU_DENIED);
537 		case 8: return (PWU_BUSY);
538 		case 9: return (PWU_SERVER_ERROR);
539 		case 4: return (PWU_NOT_FOUND);
540 		case 3: return (PWU_NO_CHANGE);
541 		case 7: return (PWU_DENIED);
542 		case 0: return (PWU_SUCCESS);
543 		default: return (PWU_SYSTEM_ERROR);
544 	}
545 }
546