xref: /illumos-gate/usr/src/lib/pam_modules/unix_cred/unix_cred.c (revision 956e8222f10bf55e45b41d8b56084f72ebc113c9)
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 (buf)) == 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 		struct passwd	rpwd;
234 		char	rpwbuf[NSS_BUFLEN_PASSWD];
235 
236 		if ((rhost == NULL || *rhost == '\0')) {
237 			if (adt_load_ttyname(tty, &termid) != 0) {
238 				syslog(LOG_AUTH | LOG_ERR,
239 				    "pam_unix_cred: cannot load ttyname %m");
240 				ret = PAM_SYSTEM_ERR;
241 				goto adt_done;
242 			}
243 		} else {
244 			if (adt_load_hostname(rhost, &termid) != 0) {
245 				syslog(LOG_AUTH | LOG_ERR,
246 				    "pam_unix_cred: cannot load hostname %m");
247 				ret = PAM_SYSTEM_ERR;
248 				goto adt_done;
249 			}
250 		}
251 		if ((ruser != NULL) && (*ruser != '\0') &&
252 		    (getpwnam_r(ruser, &rpwd, rpwbuf, sizeof (buf)) != NULL)) {
253 			/*
254 			 * set up the initial audit for user coming
255 			 * from another user
256 			 */
257 			if (adt_set_user(ah, rpwd.pw_uid, rpwd.pw_gid,
258 			    rpwd.pw_uid, rpwd.pw_gid, termid, ADT_NEW) != 0) {
259 				syslog(LOG_AUTH | LOG_ERR,
260 				    "pam_unix_cred: cannot set ruser audit "
261 				    "%m");
262 				ret = PAM_SYSTEM_ERR;
263 				goto adt_done;
264 			}
265 			if (adt_set_user(ah, pwd.pw_uid, pwd.pw_gid,
266 			    pwd.pw_uid, pwd.pw_gid, NULL,
267 			    ADT_UPDATE) != 0) {
268 				syslog(LOG_AUTH | LOG_ERR,
269 				    "pam_unix_cred: cannot merge user audit "
270 				    "%m");
271 				ret = PAM_SYSTEM_ERR;
272 				goto adt_done;
273 			}
274 			if (debug) {
275 				syslog(LOG_AUTH | LOG_DEBUG,
276 				    "pam_unix_cred: new audit set for %d:%d",
277 				    rpwd.pw_uid, pwd.pw_uid);
278 			}
279 		} else {
280 			/*
281 			 * No remote user or remote user is not a local
282 			 * user, no remote attribution, set up the initial
283 			 * audit as for direct user login
284 			 */
285 			if (adt_set_user(ah, pwd.pw_uid, pwd.pw_gid,
286 			    pwd.pw_uid, pwd.pw_gid, termid, ADT_NEW) != 0) {
287 				syslog(LOG_AUTH | LOG_ERR,
288 				    "pam_unix_cred: cannot set user audit %m");
289 				ret = PAM_SYSTEM_ERR;
290 				goto adt_done;
291 			}
292 		}
293 		if (adt_set_proc(ah) != 0) {
294 			syslog(LOG_AUTH | LOG_ERR,
295 			    "pam_unix_cred: cannot set process audit %m");
296 			ret = PAM_CRED_ERR;
297 			goto adt_done;
298 		}
299 		if (debug) {
300 			syslog(LOG_AUTH | LOG_DEBUG,
301 			    "pam_unix_cred: new audit set for %d",
302 			    pwd.pw_uid);
303 		}
304 	} else if (flags & PAM_REINITIALIZE_CRED) {
305 		if (adt_set_user(ah, pwd.pw_uid, pwd.pw_gid, pwd.pw_uid,
306 		    pwd.pw_gid, NULL, ADT_UPDATE) != 0) {
307 			syslog(LOG_AUTH | LOG_ERR,
308 			    "pam_unix_cred: cannot set user audit %m");
309 			ret = PAM_SYSTEM_ERR;
310 			goto adt_done;
311 		}
312 		if (adt_set_proc(ah) != 0) {
313 			syslog(LOG_AUTH | LOG_ERR,
314 			    "pam_unix_cred: cannot set process audit %m");
315 			ret = PAM_CRED_ERR;
316 			goto adt_done;
317 		}
318 		if (debug) {
319 			syslog(LOG_AUTH | LOG_DEBUG,
320 			    "pam_unix_cred: audit merged for %d:%d",
321 			    auid, pwd.pw_uid);
322 		}
323 	} else if (debug) {
324 		syslog(LOG_AUTH | LOG_DEBUG,
325 		    "pam_unix_cred: audit already set for %d", auid);
326 	}
327 adt_done:
328 	if (termid != NULL)
329 		free(termid);
330 	if (adt_end_session(ah) != 0) {
331 		syslog(LOG_AUTH | LOG_ERR,
332 		    "pam_unix_cred: unable to end audit session");
333 	}
334 
335 	if (ret != PAM_SUCCESS)
336 		return (ret);
337 
338 	/* Initialize the user's project */
339 	(void) pam_get_item(pamh, PAM_RESOURCE, (void **)&kvs);
340 	if (kvs != NULL) {
341 		char *tmp, *lasts, *tok;
342 
343 		kvs = tmp = strdup(kvs);
344 		if (kvs == NULL)
345 			return (PAM_BUF_ERR);
346 
347 		while ((tok = strtok_r(tmp, ";", &lasts)) != NULL) {
348 			if (strncmp(tok, PROJECT, PROJSZ) == 0) {
349 				projname = tok + PROJSZ;
350 				break;
351 			}
352 			tmp = NULL;
353 		}
354 	} else {
355 		projname = NULL;
356 	}
357 
358 	if (projname == NULL || *projname == '\0') {
359 		pproj = getdefaultproj(user, &proj, (void *)&buf,
360 		    PROJECT_BUFSZ);
361 	} else {
362 		pproj = getprojbyname(projname, &proj, (void *)&buf,
363 		    PROJECT_BUFSZ);
364 	}
365 	/* projname points into kvs, so this is the first opportunity to free */
366 	if (kvs != NULL)
367 		free(kvs);
368 	if (pproj == NULL) {
369 		syslog(LOG_AUTH | LOG_ERR,
370 		    "pam_unix_cred: no default project for user %s", user);
371 		if (!nowarn) {
372 			(void) snprintf(messages[0], sizeof (messages[0]),
373 			    dgettext(TEXT_DOMAIN, "No default project!"));
374 			(void) __pam_display_msg(pamh, PAM_ERROR_MSG,
375 			    1, messages, NULL);
376 		}
377 		return (PAM_SYSTEM_ERR);
378 	}
379 	if ((error = setproject(proj.pj_name, user, TASK_NORMAL)) != 0) {
380 		kva_t *kv_array;
381 
382 		switch (error) {
383 		case SETPROJ_ERR_TASK:
384 			if (errno == EAGAIN) {
385 				syslog(LOG_AUTH | LOG_ERR,
386 				    "pam_unix_cred: project \"%s\" resource "
387 				    "control limit has been reached",
388 				    proj.pj_name);
389 				(void) snprintf(messages[0],
390 				    sizeof (messages[0]), dgettext(
391 				    TEXT_DOMAIN,
392 				    "Resource control limit has been "
393 				    "reached"));
394 			} else {
395 				syslog(LOG_AUTH | LOG_ERR,
396 				    "pam_unix_cred: user %s could not join "
397 				    "project \"%s\": %m", user, proj.pj_name);
398 				(void) snprintf(messages[0],
399 				    sizeof (messages[0]), dgettext(
400 				    TEXT_DOMAIN,
401 				    "Could not join default project"));
402 			}
403 			if (!nowarn)
404 				(void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1,
405 				    messages, NULL);
406 			break;
407 		case SETPROJ_ERR_POOL:
408 			(void) snprintf(messages[0], sizeof (messages[0]),
409 			    dgettext(TEXT_DOMAIN,
410 			    "Could not bind to resource pool"));
411 			switch (errno) {
412 			case EACCES:
413 				syslog(LOG_AUTH | LOG_ERR,
414 				    "pam_unix_cred: project \"%s\" could not "
415 				    "bind to resource pool: No resource pool "
416 				    "accepting default bindings exists",
417 				    proj.pj_name);
418 				(void) snprintf(messages[1],
419 				    sizeof (messages[1]),
420 				    dgettext(TEXT_DOMAIN,
421 				    "No resource pool accepting "
422 				    "default bindings exists"));
423 				break;
424 			case ESRCH:
425 				syslog(LOG_AUTH | LOG_ERR,
426 				    "pam_unix_cred: project \"%s\" could not "
427 				    "bind to resource pool: The resource pool "
428 				    "is unknown", proj.pj_name);
429 				(void) snprintf(messages[1],
430 				    sizeof (messages[1]),
431 				    dgettext(TEXT_DOMAIN,
432 				    "The specified resource pool "
433 				    "is unknown"));
434 				break;
435 			default:
436 				(void) snprintf(messages[1],
437 				    sizeof (messages[1]),
438 				    dgettext(TEXT_DOMAIN,
439 				    "Failure during pool binding"));
440 				syslog(LOG_AUTH | LOG_ERR,
441 				    "pam_unix_cred: project \"%s\" could not "
442 				    "bind to resource pool: %m", proj.pj_name);
443 			}
444 			if (!nowarn)
445 				(void) __pam_display_msg(pamh, PAM_ERROR_MSG,
446 				    2, messages, NULL);
447 			break;
448 		default:
449 			/*
450 			 * Resource control assignment failed.  Unlike
451 			 * newtask(1m), we treat this as an error.
452 			 */
453 			if (error < 0) {
454 				/*
455 				 * This isn't supposed to happen, but in
456 				 * case it does, this error message
457 				 * doesn't use error as an index, like
458 				 * the others might.
459 				 */
460 				syslog(LOG_AUTH | LOG_ERR,
461 				    "pam_unix_cred: unkwown error joining "
462 				    "project \"%s\" (%d)", proj.pj_name, error);
463 				(void) snprintf(messages[0],
464 				    sizeof (messages[0]),
465 				    dgettext(TEXT_DOMAIN,
466 				    "unkwown error joining project \"%s\""
467 				    " (%d)"), proj.pj_name, error);
468 			} else if ((kv_array = _str2kva(proj.pj_attr, KV_ASSIGN,
469 			    KV_DELIMITER)) != NULL) {
470 				syslog(LOG_AUTH | LOG_ERR,
471 				    "pam_unix_cred: %s resource control "
472 				    "assignment failed for project \"%s\"",
473 				    kv_array->data[error - 1].key,
474 				    proj.pj_name);
475 				(void) snprintf(messages[0],
476 				    sizeof (messages[0]),
477 				    dgettext(TEXT_DOMAIN,
478 				    "%s resource control assignment failed for "
479 				    "project \"%s\""),
480 				    kv_array->data[error - 1].key,
481 				    proj.pj_name);
482 				_kva_free(kv_array);
483 			} else {
484 				syslog(LOG_AUTH | LOG_ERR,
485 				    "pam_unix_cred: resource control "
486 				    "assignment failed for project \"%s\""
487 				    "attribute %d", proj.pj_name, error);
488 				(void) snprintf(messages[0],
489 				    sizeof (messages[0]),
490 				    dgettext(TEXT_DOMAIN,
491 				    "resource control assignment failed for "
492 				    "project \"%s\" attribute %d"),
493 				    proj.pj_name, error);
494 			}
495 			if (!nowarn)
496 				(void) __pam_display_msg(pamh, PAM_ERROR_MSG,
497 				    1, messages, NULL);
498 		}
499 		return (PAM_SYSTEM_ERR);
500 	}
501 
502 	ua = getusernam(user);
503 
504 	(void) defopen(AUTH_POLICY);
505 
506 	def = lim = NULL;
507 
508 	if (getset(USERATTR_LIMPRIV_KW, DEF_LIMITPRIV, ua, &lim) != 0 ||
509 	    getset(USERATTR_DFLTPRIV_KW, DEF_DFLTPRIV, ua, &def) != 0) {
510 		ret = PAM_SYSTEM_ERR;
511 		goto out;
512 	}
513 
514 	if (def == NULL) {
515 		def = priv_str_to_set("basic", ",", NULL);
516 		if (pathconf("/", _PC_CHOWN_RESTRICTED) == -1)
517 			(void) priv_addset(def, PRIV_FILE_CHOWN_SELF);
518 	}
519 	/*
520 	 * We set privilege awareness here so that I gets copied to
521 	 * P & E when the final setuid(uid) happens.
522 	 */
523 	(void) setpflags(PRIV_AWARE, 1);
524 	if (setppriv(PRIV_SET, PRIV_INHERITABLE, def) != 0) {
525 		syslog(LOG_AUTH | LOG_ERR,
526 			    "pam_setcred: setppriv(defaultpriv) failed: %m");
527 		ret = PAM_CRED_ERR;
528 	}
529 
530 	if (lim != NULL) {
531 		/*
532 		 * In order not to suprise certain applications, we
533 		 * need to retain privilege awareness and thus we must
534 		 * also set P and E.
535 		 */
536 		if (setppriv(PRIV_SET, PRIV_LIMIT, lim) != 0 ||
537 		    setppriv(PRIV_SET, PRIV_PERMITTED, lim) != 0) {
538 			syslog(LOG_AUTH | LOG_ERR,
539 				"pam_setcred: setppriv(limitpriv) failed: %m");
540 			ret = PAM_CRED_ERR;
541 		}
542 	}
543 	(void) setpflags(PRIV_AWARE, 0);
544 
545 out:
546 	(void) defopen(NULL);
547 
548 	if (ua != NULL)
549 		free_userattr(ua);
550 	if (lim != NULL)
551 		priv_freeset(lim);
552 	if (def != NULL)
553 		priv_freeset(def);
554 
555 	return (ret);
556 }
557