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