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