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