xref: /illumos-gate/usr/src/lib/pam_modules/unix_cred/unix_cred.c (revision a07094369b21309434206d9b3601d162693466fc)
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 <nss_dbdefs.h>
30 #include <pwd.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <syslog.h>
34 #include <unistd.h>
35 #include <auth_attr.h>
36 #include <deflt.h>
37 #include <priv.h>
38 #include <secdb.h>
39 #include <user_attr.h>
40 #include <sys/task.h>
41 #include <libintl.h>
42 #include <project.h>
43 #include <errno.h>
44 
45 #include <bsm/adt.h>
46 #include <bsm/adt_event.h>	/* adt_get_auid() */
47 
48 #include <security/pam_appl.h>
49 #include <security/pam_modules.h>
50 #include <security/pam_impl.h>
51 
52 #define	PROJECT		"project="
53 #define	PROJSZ		(sizeof (PROJECT) - 1)
54 
55 /*
56  *	unix_cred - PAM auth modules must contain both pam_sm_authenticate
57  *		and pam_sm_setcred.  Some other auth module is responsible
58  *		for authentication (e.g., pam_unix_auth.so), this module
59  *		only implements pam_sm_setcred so that the authentication
60  *		can be separated without knowledge of the Solaris Unix style
61  *		credential setting.
62  *		Solaris Unix style credential setting includes initializing
63  *		the audit characteristics if not already initialized and
64  *		setting the user's default and limit privileges.
65  */
66 
67 /*
68  *	unix_cred - pam_sm_authenticate
69  *
70  *	Returns	PAM_IGNORE.
71  */
72 
73 /*ARGSUSED*/
74 int
75 pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
76 {
77 	return (PAM_IGNORE);
78 }
79 
80 /*
81  * Obtain a privilege set "keyname" from userattr; if none is present,
82  * fall back to the default, "defname".
83  */
84 static int
85 getset(char *keyname, char *defname, userattr_t *ua, priv_set_t **res)
86 {
87 	char *str;
88 	priv_set_t *tmp;
89 
90 	if ((ua == NULL || ua->attr == NULL ||
91 	    (str = kva_match(ua->attr, keyname)) == NULL) &&
92 	    (str = defread(defname)) == NULL)
93 		return (0);
94 
95 	tmp = priv_str_to_set(str, ",", NULL);
96 
97 	if (tmp == NULL) {
98 		syslog(LOG_AUTH|LOG_ERR,
99 		    "pam_setcred: bad privilege specification: %s\n", str);
100 		priv_freeset(tmp);
101 		return (-1);
102 	}
103 	*res = tmp;
104 	return (0);
105 }
106 
107 /*
108  *	unix_cred - pam_sm_setcred
109  *
110  *	Entry flags = 	PAM_ESTABLISH_CRED, set up Solaris Unix cred.
111  *			PAM_DELETE_CRED, NOP, return PAM_SUCCESS.
112  *			PAM_REINITIALIZE_CRED, set up Solaris Unix cred,
113  *				or merge the current context with the new
114  *				user.
115  *			PAM_REFRESH_CRED, set up Solaris Unix cred.
116  *			PAM_SILENT, print no messages to user.
117  *
118  *	Returns	PAM_SUCCESS, if all successful.
119  *		PAM_CRED_ERR, if unable to set credentials.
120  *		PAM_USER_UNKNOWN, if PAM_USER not set, or unable to find
121  *			user in databases.
122  *		PAM_SYSTEM_ERR, if no valid flag, or unable to get/set
123  *			user's audit state.
124  */
125 
126 int
127 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
128 {
129 	int	i;
130 	int	debug = 0;
131 	uint_t	nowarn = flags & PAM_SILENT;
132 	int	ret = PAM_SUCCESS;
133 	char	*user;
134 	char	*ruser;
135 	char	*rhost;
136 	char	*tty;
137 	au_id_t	auid;
138 	adt_session_data_t *ah;
139 	adt_termid_t	*termid = NULL;
140 	userattr_t	*ua;
141 	priv_set_t	*lim, *def;
142 	char		messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
143 	char		buf[PROJECT_BUFSZ];
144 	struct project	proj, *pproj;
145 	int		error;
146 	char		*projname;
147 	char		*kvs;
148 	struct passwd	pwd;
149 	char		pwbuf[NSS_BUFLEN_PASSWD];
150 
151 	for (i = 0; i < argc; i++) {
152 		if (strcmp(argv[i], "debug") == 0)
153 			debug = 1;
154 		else if (strcmp(argv[i], "nowarn") == 0)
155 			nowarn |= 1;
156 	}
157 
158 	if (debug)
159 		syslog(LOG_AUTH | LOG_DEBUG,
160 		    "pam_unix_cred: pam_sm_setcred(flags = %x, argc= %d)",
161 		    flags, argc);
162 
163 	(void) pam_get_item(pamh, PAM_USER, (void **)&user);
164 
165 	if (user == NULL || *user == '\0') {
166 		syslog(LOG_AUTH | LOG_ERR,
167 		    "pam_unix_cred: USER NULL or empty!\n");
168 		return (PAM_USER_UNKNOWN);
169 	}
170 	(void) pam_get_item(pamh, PAM_RUSER, (void **)&ruser);
171 	(void) pam_get_item(pamh, PAM_RHOST, (void **)&rhost);
172 	(void) pam_get_item(pamh, PAM_TTY, (void **)&tty);
173 	if (debug)
174 		syslog(LOG_AUTH | LOG_DEBUG,
175 		    "pam_unix_cred: user = %s, ruser = %s, rhost = %s, "
176 		    "tty = %s", user,
177 		    (ruser == NULL) ? "NULL" : (*ruser == '\0') ? "ZERO" :
178 		    ruser,
179 		    (rhost == NULL) ? "NULL" : (*rhost == '\0') ? "ZERO" :
180 		    rhost,
181 		    (tty == NULL) ? "NULL" : (*tty == '\0') ? "ZERO" :
182 		    tty);
183 
184 	/* validate flags */
185 	switch (flags & (PAM_ESTABLISH_CRED | PAM_DELETE_CRED |
186 	    PAM_REINITIALIZE_CRED | PAM_REFRESH_CRED)) {
187 	case 0:
188 		/* set default flag */
189 		flags |= PAM_ESTABLISH_CRED;
190 		break;
191 	case PAM_ESTABLISH_CRED:
192 	case PAM_REINITIALIZE_CRED:
193 	case PAM_REFRESH_CRED:
194 		break;
195 	case PAM_DELETE_CRED:
196 		return (PAM_SUCCESS);
197 	default:
198 		syslog(LOG_AUTH | LOG_ERR,
199 		    "pam_unix_cred: invalid flags %x", flags);
200 		return (PAM_SYSTEM_ERR);
201 	}
202 
203 	/*
204 	 * if auditing on and process audit state not set,
205 	 * setup audit context for process.
206 	 */
207 	if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
208 		syslog(LOG_AUTH | LOG_ERR,
209 		    "pam_unix_cred: cannot create start audit session %m");
210 		return (PAM_SYSTEM_ERR);
211 	}
212 	adt_get_auid(ah, &auid);
213 	if (debug) {
214 		int	auditstate;
215 
216 		if (auditon(A_GETCOND, (caddr_t)&auditstate,
217 		    sizeof (auditstate)) != 0) {
218 			auditstate = AUC_DISABLED;
219 		}
220 		syslog(LOG_AUTH | LOG_DEBUG,
221 		    "pam_unix_cred: state = %d, auid = %d", auditstate,
222 		    auid);
223 	}
224 	if (getpwnam_r(user, &pwd, pwbuf, sizeof (pwbuf)) == NULL) {
225 		syslog(LOG_AUTH | LOG_ERR,
226 		    "pam_unix_cred: cannot get passwd entry for user = %s",
227 		    user);
228 		ret = PAM_USER_UNKNOWN;
229 		goto adt_done;
230 	}
231 
232 	if ((auid == AU_NOAUDITID) &&
233 	    (flags & PAM_ESTABLISH_CRED)) {
234 		struct passwd	rpwd;
235 		char	rpwbuf[NSS_BUFLEN_PASSWD];
236 
237 		if ((rhost == NULL || *rhost == '\0')) {
238 			if (adt_load_ttyname(tty, &termid) != 0) {
239 				syslog(LOG_AUTH | LOG_ERR,
240 				    "pam_unix_cred: cannot load ttyname %m");
241 				ret = PAM_SYSTEM_ERR;
242 				goto adt_done;
243 			}
244 		} else {
245 			if (adt_load_hostname(rhost, &termid) != 0) {
246 				syslog(LOG_AUTH | LOG_ERR,
247 				    "pam_unix_cred: cannot load hostname %m");
248 				ret = PAM_SYSTEM_ERR;
249 				goto adt_done;
250 			}
251 		}
252 		if ((ruser != NULL) && (*ruser != '\0') &&
253 		    (getpwnam_r(ruser, &rpwd, rpwbuf,
254 		    sizeof (rpwbuf)) != NULL)) {
255 			/*
256 			 * set up the initial audit for user coming
257 			 * from another user
258 			 */
259 			if (adt_set_user(ah, rpwd.pw_uid, rpwd.pw_gid,
260 			    rpwd.pw_uid, rpwd.pw_gid, termid, ADT_NEW) != 0) {
261 				syslog(LOG_AUTH | LOG_ERR,
262 				    "pam_unix_cred: cannot set ruser audit "
263 				    "%m");
264 				ret = PAM_SYSTEM_ERR;
265 				goto adt_done;
266 			}
267 			if (adt_set_user(ah, pwd.pw_uid, pwd.pw_gid,
268 			    pwd.pw_uid, pwd.pw_gid, NULL,
269 			    ADT_UPDATE) != 0) {
270 				syslog(LOG_AUTH | LOG_ERR,
271 				    "pam_unix_cred: cannot merge user audit "
272 				    "%m");
273 				ret = PAM_SYSTEM_ERR;
274 				goto adt_done;
275 			}
276 			if (debug) {
277 				syslog(LOG_AUTH | LOG_DEBUG,
278 				    "pam_unix_cred: new audit set for %d:%d",
279 				    rpwd.pw_uid, pwd.pw_uid);
280 			}
281 		} else {
282 			/*
283 			 * No remote user or remote user is not a local
284 			 * user, no remote attribution, set up the initial
285 			 * audit as for direct user login
286 			 */
287 			if (adt_set_user(ah, pwd.pw_uid, pwd.pw_gid,
288 			    pwd.pw_uid, pwd.pw_gid, termid, ADT_NEW) != 0) {
289 				syslog(LOG_AUTH | LOG_ERR,
290 				    "pam_unix_cred: cannot set user audit %m");
291 				ret = PAM_SYSTEM_ERR;
292 				goto adt_done;
293 			}
294 		}
295 		if (adt_set_proc(ah) != 0) {
296 			syslog(LOG_AUTH | LOG_ERR,
297 			    "pam_unix_cred: cannot set process audit %m");
298 			ret = PAM_CRED_ERR;
299 			goto adt_done;
300 		}
301 		if (debug) {
302 			syslog(LOG_AUTH | LOG_DEBUG,
303 			    "pam_unix_cred: new audit set for %d",
304 			    pwd.pw_uid);
305 		}
306 	} else if ((auid != AU_NOAUDITID) &&
307 	    (flags & PAM_REINITIALIZE_CRED)) {
308 		if (adt_set_user(ah, pwd.pw_uid, pwd.pw_gid, pwd.pw_uid,
309 		    pwd.pw_gid, NULL, ADT_UPDATE) != 0) {
310 			syslog(LOG_AUTH | LOG_ERR,
311 			    "pam_unix_cred: cannot set user audit %m");
312 			ret = PAM_SYSTEM_ERR;
313 			goto adt_done;
314 		}
315 		if (adt_set_proc(ah) != 0) {
316 			syslog(LOG_AUTH | LOG_ERR,
317 			    "pam_unix_cred: cannot set process audit %m");
318 			ret = PAM_CRED_ERR;
319 			goto adt_done;
320 		}
321 		if (debug) {
322 			syslog(LOG_AUTH | LOG_DEBUG,
323 			    "pam_unix_cred: audit merged for %d:%d",
324 			    auid, pwd.pw_uid);
325 		}
326 	} else if (debug) {
327 		syslog(LOG_AUTH | LOG_DEBUG,
328 		    "pam_unix_cred: audit already set for %d", auid);
329 	}
330 adt_done:
331 	if (termid != NULL)
332 		free(termid);
333 	if (adt_end_session(ah) != 0) {
334 		syslog(LOG_AUTH | LOG_ERR,
335 		    "pam_unix_cred: unable to end audit session");
336 	}
337 
338 	if (ret != PAM_SUCCESS)
339 		return (ret);
340 
341 	/* Initialize the user's project */
342 	(void) pam_get_item(pamh, PAM_RESOURCE, (void **)&kvs);
343 	if (kvs != NULL) {
344 		char *tmp, *lasts, *tok;
345 
346 		kvs = tmp = strdup(kvs);
347 		if (kvs == NULL)
348 			return (PAM_BUF_ERR);
349 
350 		while ((tok = strtok_r(tmp, ";", &lasts)) != NULL) {
351 			if (strncmp(tok, PROJECT, PROJSZ) == 0) {
352 				projname = tok + PROJSZ;
353 				break;
354 			}
355 			tmp = NULL;
356 		}
357 	} else {
358 		projname = NULL;
359 	}
360 
361 	if (projname == NULL || *projname == '\0') {
362 		pproj = getdefaultproj(user, &proj, (void *)&buf,
363 		    PROJECT_BUFSZ);
364 	} else {
365 		pproj = getprojbyname(projname, &proj, (void *)&buf,
366 		    PROJECT_BUFSZ);
367 	}
368 	/* projname points into kvs, so this is the first opportunity to free */
369 	if (kvs != NULL)
370 		free(kvs);
371 	if (pproj == NULL) {
372 		syslog(LOG_AUTH | LOG_ERR,
373 		    "pam_unix_cred: no default project for user %s", user);
374 		if (!nowarn) {
375 			(void) snprintf(messages[0], sizeof (messages[0]),
376 			    dgettext(TEXT_DOMAIN, "No default project!"));
377 			(void) __pam_display_msg(pamh, PAM_ERROR_MSG,
378 			    1, messages, NULL);
379 		}
380 		return (PAM_SYSTEM_ERR);
381 	}
382 	if ((error = setproject(proj.pj_name, user, TASK_NORMAL)) != 0) {
383 		kva_t *kv_array;
384 
385 		switch (error) {
386 		case SETPROJ_ERR_TASK:
387 			if (errno == EAGAIN) {
388 				syslog(LOG_AUTH | LOG_ERR,
389 				    "pam_unix_cred: project \"%s\" resource "
390 				    "control limit has been reached",
391 				    proj.pj_name);
392 				(void) snprintf(messages[0],
393 				    sizeof (messages[0]), dgettext(
394 				    TEXT_DOMAIN,
395 				    "Resource control limit has been "
396 				    "reached"));
397 			} else {
398 				syslog(LOG_AUTH | LOG_ERR,
399 				    "pam_unix_cred: user %s could not join "
400 				    "project \"%s\": %m", user, proj.pj_name);
401 				(void) snprintf(messages[0],
402 				    sizeof (messages[0]), dgettext(
403 				    TEXT_DOMAIN,
404 				    "Could not join default project"));
405 			}
406 			if (!nowarn)
407 				(void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1,
408 				    messages, NULL);
409 			break;
410 		case SETPROJ_ERR_POOL:
411 			(void) snprintf(messages[0], sizeof (messages[0]),
412 			    dgettext(TEXT_DOMAIN,
413 			    "Could not bind to resource pool"));
414 			switch (errno) {
415 			case EACCES:
416 				syslog(LOG_AUTH | LOG_ERR,
417 				    "pam_unix_cred: project \"%s\" could not "
418 				    "bind to resource pool: No resource pool "
419 				    "accepting default bindings exists",
420 				    proj.pj_name);
421 				(void) snprintf(messages[1],
422 				    sizeof (messages[1]),
423 				    dgettext(TEXT_DOMAIN,
424 				    "No resource pool accepting "
425 				    "default bindings exists"));
426 				break;
427 			case ESRCH:
428 				syslog(LOG_AUTH | LOG_ERR,
429 				    "pam_unix_cred: project \"%s\" could not "
430 				    "bind to resource pool: The resource pool "
431 				    "is unknown", proj.pj_name);
432 				(void) snprintf(messages[1],
433 				    sizeof (messages[1]),
434 				    dgettext(TEXT_DOMAIN,
435 				    "The specified resource pool "
436 				    "is unknown"));
437 				break;
438 			default:
439 				(void) snprintf(messages[1],
440 				    sizeof (messages[1]),
441 				    dgettext(TEXT_DOMAIN,
442 				    "Failure during pool binding"));
443 				syslog(LOG_AUTH | LOG_ERR,
444 				    "pam_unix_cred: project \"%s\" could not "
445 				    "bind to resource pool: %m", proj.pj_name);
446 			}
447 			if (!nowarn)
448 				(void) __pam_display_msg(pamh, PAM_ERROR_MSG,
449 				    2, messages, NULL);
450 			break;
451 		default:
452 			/*
453 			 * Resource control assignment failed.  Unlike
454 			 * newtask(1m), we treat this as an error.
455 			 */
456 			if (error < 0) {
457 				/*
458 				 * This isn't supposed to happen, but in
459 				 * case it does, this error message
460 				 * doesn't use error as an index, like
461 				 * the others might.
462 				 */
463 				syslog(LOG_AUTH | LOG_ERR,
464 				    "pam_unix_cred: unkwown error joining "
465 				    "project \"%s\" (%d)", proj.pj_name, error);
466 				(void) snprintf(messages[0],
467 				    sizeof (messages[0]),
468 				    dgettext(TEXT_DOMAIN,
469 				    "unkwown error joining project \"%s\""
470 				    " (%d)"), proj.pj_name, error);
471 			} else if ((kv_array = _str2kva(proj.pj_attr, KV_ASSIGN,
472 			    KV_DELIMITER)) != NULL) {
473 				syslog(LOG_AUTH | LOG_ERR,
474 				    "pam_unix_cred: %s resource control "
475 				    "assignment failed for project \"%s\"",
476 				    kv_array->data[error - 1].key,
477 				    proj.pj_name);
478 				(void) snprintf(messages[0],
479 				    sizeof (messages[0]),
480 				    dgettext(TEXT_DOMAIN,
481 				    "%s resource control assignment failed for "
482 				    "project \"%s\""),
483 				    kv_array->data[error - 1].key,
484 				    proj.pj_name);
485 				_kva_free(kv_array);
486 			} else {
487 				syslog(LOG_AUTH | LOG_ERR,
488 				    "pam_unix_cred: resource control "
489 				    "assignment failed for project \"%s\""
490 				    "attribute %d", proj.pj_name, error);
491 				(void) snprintf(messages[0],
492 				    sizeof (messages[0]),
493 				    dgettext(TEXT_DOMAIN,
494 				    "resource control assignment failed for "
495 				    "project \"%s\" attribute %d"),
496 				    proj.pj_name, error);
497 			}
498 			if (!nowarn)
499 				(void) __pam_display_msg(pamh, PAM_ERROR_MSG,
500 				    1, messages, NULL);
501 		}
502 		return (PAM_SYSTEM_ERR);
503 	}
504 
505 	ua = getusernam(user);
506 
507 	(void) defopen(AUTH_POLICY);
508 
509 	def = lim = NULL;
510 
511 	if (getset(USERATTR_LIMPRIV_KW, DEF_LIMITPRIV, ua, &lim) != 0 ||
512 	    getset(USERATTR_DFLTPRIV_KW, DEF_DFLTPRIV, ua, &def) != 0) {
513 		ret = PAM_SYSTEM_ERR;
514 		goto out;
515 	}
516 
517 	if (def == NULL) {
518 		def = priv_str_to_set("basic", ",", NULL);
519 		if (pathconf("/", _PC_CHOWN_RESTRICTED) == -1)
520 			(void) priv_addset(def, PRIV_FILE_CHOWN_SELF);
521 	}
522 	/*
523 	 * We set privilege awareness here so that I gets copied to
524 	 * P & E when the final setuid(uid) happens.
525 	 */
526 	(void) setpflags(PRIV_AWARE, 1);
527 	if (setppriv(PRIV_SET, PRIV_INHERITABLE, def) != 0) {
528 		syslog(LOG_AUTH | LOG_ERR,
529 			    "pam_setcred: setppriv(defaultpriv) failed: %m");
530 		ret = PAM_CRED_ERR;
531 	}
532 
533 	if (lim != NULL) {
534 		/*
535 		 * In order not to suprise certain applications, we
536 		 * need to retain privilege awareness and thus we must
537 		 * also set P and E.
538 		 */
539 		if (setppriv(PRIV_SET, PRIV_LIMIT, lim) != 0 ||
540 		    setppriv(PRIV_SET, PRIV_PERMITTED, lim) != 0) {
541 			syslog(LOG_AUTH | LOG_ERR,
542 				"pam_setcred: setppriv(limitpriv) failed: %m");
543 			ret = PAM_CRED_ERR;
544 		}
545 	}
546 	(void) setpflags(PRIV_AWARE, 0);
547 
548 out:
549 	(void) defopen(NULL);
550 
551 	if (ua != NULL)
552 		free_userattr(ua);
553 	if (lim != NULL)
554 		priv_freeset(lim);
555 	if (def != NULL)
556 		priv_freeset(def);
557 
558 	return (ret);
559 }
560