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.
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.
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]
22 * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
25 #include <nss_dbdefs.h>
31 #include <auth_attr.h>
35 #include <user_attr.h>
43 #include <bsm/adt_event.h> /* adt_get_auid() */
45 #include <security/pam_appl.h>
46 #include <security/pam_modules.h>
47 #include <security/pam_impl.h>
49 #define PROJECT "project="
50 #define PROJSZ (sizeof (PROJECT) - 1)
53 * unix_cred - PAM auth modules must contain both pam_sm_authenticate
54 * and pam_sm_setcred. Some other auth module is responsible
55 * for authentication (e.g., pam_unix_auth.so), this module
56 * only implements pam_sm_setcred so that the authentication
57 * can be separated without knowledge of the Solaris Unix style
59 * Solaris Unix style credential setting includes initializing
60 * the audit characteristics if not already initialized and
61 * setting the user's default and limit privileges.
65 * unix_cred - pam_sm_authenticate
72 pam_sm_authenticate(pam_handle_t
*pamh
, int flags
, int argc
, const char **argv
)
78 * Set the privilege set. The attributes are enumerated by _enum_attrs,
79 * including the attribues user_attr, prof_attr and policy.conf
82 getset(char *str
, priv_set_t
**res
)
91 len
= strlen(str
) + 1;
93 (void) memset(badp
, '\0', len
);
96 tmp
= priv_str_to_set(str
, ",", &endp
);
101 /* Now remove the bad privilege endp points to */
102 q
= strchr(endp
, ',');
104 q
= endp
+ strlen(endp
);
107 (void) strlcat(badp
, ",", len
);
108 /* Memset above guarantees NUL termination */
110 (void) strncat(badp
, endp
, q
- endp
);
111 /* excise bad privilege; strtok ignores 2x sep */
112 (void) memmove((void *)endp
, q
, strlen(q
) + 1);
114 } while (tmp
== NULL
&& *str
!= '\0');
117 syslog(LOG_AUTH
|LOG_ERR
,
118 "pam_setcred: can't parse privilege specification: %m\n");
120 } else if (*badp
!= '\0') {
121 syslog(LOG_AUTH
|LOG_DEBUG
,
122 "pam_setcred: unrecognized privilege(s): %s\n", badp
);
128 typedef struct deflim
{
135 finddeflim(const char *name
, kva_t
*kva
, void *ctxt
, void *pres
)
137 deflim_t
*pdef
= pres
;
140 if (pdef
->def
== NULL
) {
141 val
= kva_match(kva
, USERATTR_DFLTPRIV_KW
);
143 pdef
->def
= strdup(val
);
145 if (pdef
->lim
== NULL
) {
146 val
= kva_match(kva
, USERATTR_LIMPRIV_KW
);
148 pdef
->lim
= strdup(val
);
150 return (pdef
->lim
!= NULL
&& pdef
->def
!= NULL
);
154 * unix_cred - pam_sm_setcred
156 * Entry flags = PAM_ESTABLISH_CRED, set up Solaris Unix cred.
157 * PAM_DELETE_CRED, NOP, return PAM_SUCCESS.
158 * PAM_REINITIALIZE_CRED, set up Solaris Unix cred,
159 * or merge the current context with the new
161 * PAM_REFRESH_CRED, set up Solaris Unix cred.
162 * PAM_SILENT, print no messages to user.
164 * Returns PAM_SUCCESS, if all successful.
165 * PAM_CRED_ERR, if unable to set credentials.
166 * PAM_USER_UNKNOWN, if PAM_USER not set, or unable to find
168 * PAM_SYSTEM_ERR, if no valid flag, or unable to get/set
169 * user's audit state.
173 pam_sm_setcred(pam_handle_t
*pamh
, int flags
, int argc
, const char **argv
)
177 uint_t nowarn
= flags
& PAM_SILENT
;
178 int ret
= PAM_SUCCESS
;
184 adt_session_data_t
*ah
;
185 adt_termid_t
*termid
= NULL
;
186 priv_set_t
*lim
, *def
, *tset
;
187 char messages
[PAM_MAX_NUM_MSG
][PAM_MAX_MSG_SIZE
];
188 char buf
[PROJECT_BUFSZ
];
189 struct project proj
, *pproj
;
195 char pwbuf
[NSS_BUFLEN_PASSWD
];
198 for (i
= 0; i
< argc
; i
++) {
199 if (strcmp(argv
[i
], "debug") == 0)
201 else if (strcmp(argv
[i
], "nowarn") == 0)
206 syslog(LOG_AUTH
| LOG_DEBUG
,
207 "pam_unix_cred: pam_sm_setcred(flags = %x, argc= %d)",
210 (void) pam_get_item(pamh
, PAM_USER
, (void **)&user
);
212 if (user
== NULL
|| *user
== '\0') {
213 syslog(LOG_AUTH
| LOG_ERR
,
214 "pam_unix_cred: USER NULL or empty!\n");
215 return (PAM_USER_UNKNOWN
);
217 (void) pam_get_item(pamh
, PAM_AUSER
, (void **)&auser
);
218 (void) pam_get_item(pamh
, PAM_RHOST
, (void **)&rhost
);
219 (void) pam_get_item(pamh
, PAM_TTY
, (void **)&tty
);
221 syslog(LOG_AUTH
| LOG_DEBUG
,
222 "pam_unix_cred: user = %s, auser = %s, rhost = %s, "
224 (auser
== NULL
) ? "NULL" : (*auser
== '\0') ? "ZERO" :
226 (rhost
== NULL
) ? "NULL" : (*rhost
== '\0') ? "ZERO" :
228 (tty
== NULL
) ? "NULL" : (*tty
== '\0') ? "ZERO" :
232 switch (flags
& (PAM_ESTABLISH_CRED
| PAM_DELETE_CRED
|
233 PAM_REINITIALIZE_CRED
| PAM_REFRESH_CRED
)) {
235 /* set default flag */
236 flags
|= PAM_ESTABLISH_CRED
;
238 case PAM_ESTABLISH_CRED
:
239 case PAM_REINITIALIZE_CRED
:
240 case PAM_REFRESH_CRED
:
242 case PAM_DELETE_CRED
:
243 return (PAM_SUCCESS
);
245 syslog(LOG_AUTH
| LOG_ERR
,
246 "pam_unix_cred: invalid flags %x", flags
);
247 return (PAM_SYSTEM_ERR
);
251 * if auditing on and process audit state not set,
252 * setup audit context for process.
254 if (adt_start_session(&ah
, NULL
, ADT_USE_PROC_DATA
) != 0) {
255 syslog(LOG_AUTH
| LOG_ERR
,
256 "pam_unix_cred: cannot create start audit session %m");
257 return (PAM_SYSTEM_ERR
);
259 adt_get_auid(ah
, &auid
);
263 if (auditon(A_GETCOND
, (caddr_t
)&auditstate
,
264 sizeof (auditstate
)) != 0) {
265 auditstate
= AUC_DISABLED
;
267 syslog(LOG_AUTH
| LOG_DEBUG
,
268 "pam_unix_cred: state = %d, auid = %d", auditstate
,
271 getpwnam_r(user
, &pwd
, pwbuf
, sizeof (pwbuf
), &pwdp
);
273 syslog(LOG_AUTH
| LOG_ERR
,
274 "pam_unix_cred: cannot get passwd entry for user = %s",
276 ret
= PAM_USER_UNKNOWN
;
280 if ((auid
== AU_NOAUDITID
) &&
281 (flags
& PAM_ESTABLISH_CRED
)) {
283 char apwbuf
[NSS_BUFLEN_PASSWD
];
286 if ((rhost
== NULL
|| *rhost
== '\0')) {
287 if (adt_load_ttyname(tty
, &termid
) != 0) {
288 if (errno
== ENETDOWN
) {
290 * tolerate not being able to
291 * translate local hostname
292 * to a termid -- it will be
295 syslog(LOG_AUTH
| LOG_ERR
,
296 "pam_unix_cred: cannot load "
297 "ttyname: %m, continuing.");
299 } else if (errno
!= 0) {
300 syslog(LOG_AUTH
| LOG_ERR
,
301 "pam_unix_cred: cannot load "
304 syslog(LOG_AUTH
| LOG_ERR
,
305 "pam_unix_cred: cannot load "
308 ret
= PAM_SYSTEM_ERR
;
312 if (adt_load_hostname(rhost
, &termid
) != 0) {
314 syslog(LOG_AUTH
| LOG_ERR
,
315 "pam_unix_cred: cannot load "
318 syslog(LOG_AUTH
| LOG_ERR
,
319 "pam_unix_cred: cannot load "
322 ret
= PAM_SYSTEM_ERR
;
327 if ((auser
!= NULL
) && (*auser
!= '\0') &&
328 (getpwnam_r(auser
, &apwd
, apwbuf
,
329 sizeof (apwbuf
), &pwdp
) == 0 && pwdp
!= NULL
)) {
331 * set up the initial audit for user coming
334 if (adt_set_user(ah
, apwd
.pw_uid
, apwd
.pw_gid
,
335 apwd
.pw_uid
, apwd
.pw_gid
, termid
, ADT_NEW
) != 0) {
336 syslog(LOG_AUTH
| LOG_ERR
,
337 "pam_unix_cred: cannot set auser audit "
339 ret
= PAM_SYSTEM_ERR
;
342 if (adt_set_user(ah
, pwd
.pw_uid
, pwd
.pw_gid
,
343 pwd
.pw_uid
, pwd
.pw_gid
, NULL
,
345 syslog(LOG_AUTH
| LOG_ERR
,
346 "pam_unix_cred: cannot merge user audit "
348 ret
= PAM_SYSTEM_ERR
;
352 syslog(LOG_AUTH
| LOG_DEBUG
,
353 "pam_unix_cred: new audit set for %d:%d",
354 apwd
.pw_uid
, pwd
.pw_uid
);
358 * No authenticated user or authenticated user is
359 * not a local user, no remote attribution, set
360 * up the initial audit as for direct user login
362 if (adt_set_user(ah
, pwd
.pw_uid
, pwd
.pw_gid
,
363 pwd
.pw_uid
, pwd
.pw_gid
, termid
, ADT_NEW
) != 0) {
364 syslog(LOG_AUTH
| LOG_ERR
,
365 "pam_unix_cred: cannot set user audit %m");
366 ret
= PAM_SYSTEM_ERR
;
370 if (adt_set_proc(ah
) != 0) {
371 syslog(LOG_AUTH
| LOG_ERR
,
372 "pam_unix_cred: cannot set process audit %m");
377 syslog(LOG_AUTH
| LOG_DEBUG
,
378 "pam_unix_cred: new audit set for %d",
381 } else if ((auid
!= AU_NOAUDITID
) &&
382 (flags
& PAM_REINITIALIZE_CRED
)) {
383 if (adt_set_user(ah
, pwd
.pw_uid
, pwd
.pw_gid
, pwd
.pw_uid
,
384 pwd
.pw_gid
, NULL
, ADT_UPDATE
) != 0) {
385 syslog(LOG_AUTH
| LOG_ERR
,
386 "pam_unix_cred: cannot set user audit %m");
387 ret
= PAM_SYSTEM_ERR
;
390 if (adt_set_proc(ah
) != 0) {
391 syslog(LOG_AUTH
| LOG_ERR
,
392 "pam_unix_cred: cannot set process audit %m");
397 syslog(LOG_AUTH
| LOG_DEBUG
,
398 "pam_unix_cred: audit merged for %d:%d",
402 syslog(LOG_AUTH
| LOG_DEBUG
,
403 "pam_unix_cred: audit already set for %d", auid
);
407 if (adt_end_session(ah
) != 0) {
408 syslog(LOG_AUTH
| LOG_ERR
,
409 "pam_unix_cred: unable to end audit session");
412 if (ret
!= PAM_SUCCESS
)
415 /* Initialize the user's project */
416 (void) pam_get_item(pamh
, PAM_RESOURCE
, (void **)&kvs
);
418 char *tmp
, *lasts
, *tok
;
420 kvs
= tmp
= strdup(kvs
);
422 return (PAM_BUF_ERR
);
424 while ((tok
= strtok_r(tmp
, ";", &lasts
)) != NULL
) {
425 if (strncmp(tok
, PROJECT
, PROJSZ
) == 0) {
426 projname
= tok
+ PROJSZ
;
435 if (projname
== NULL
|| *projname
== '\0') {
436 pproj
= getdefaultproj(user
, &proj
, (void *)&buf
,
439 pproj
= getprojbyname(projname
, &proj
, (void *)&buf
,
442 /* projname points into kvs, so this is the first opportunity to free */
445 syslog(LOG_AUTH
| LOG_ERR
,
446 "pam_unix_cred: no default project for user %s", user
);
448 (void) snprintf(messages
[0], sizeof (messages
[0]),
449 dgettext(TEXT_DOMAIN
, "No default project!"));
450 (void) __pam_display_msg(pamh
, PAM_ERROR_MSG
,
453 return (PAM_SYSTEM_ERR
);
455 if ((error
= setproject(proj
.pj_name
, user
, TASK_NORMAL
)) != 0) {
459 case SETPROJ_ERR_TASK
:
460 if (errno
== EAGAIN
) {
461 syslog(LOG_AUTH
| LOG_ERR
,
462 "pam_unix_cred: project \"%s\" resource "
463 "control limit has been reached",
465 (void) snprintf(messages
[0],
466 sizeof (messages
[0]), dgettext(
468 "Resource control limit has been "
471 syslog(LOG_AUTH
| LOG_ERR
,
472 "pam_unix_cred: user %s could not join "
473 "project \"%s\": %m", user
, proj
.pj_name
);
474 (void) snprintf(messages
[0],
475 sizeof (messages
[0]), dgettext(
477 "Could not join default project"));
480 (void) __pam_display_msg(pamh
, PAM_ERROR_MSG
, 1,
483 case SETPROJ_ERR_POOL
:
484 (void) snprintf(messages
[0], sizeof (messages
[0]),
485 dgettext(TEXT_DOMAIN
,
486 "Could not bind to resource pool"));
489 syslog(LOG_AUTH
| LOG_ERR
,
490 "pam_unix_cred: project \"%s\" could not "
491 "bind to resource pool: No resource pool "
492 "accepting default bindings exists",
494 (void) snprintf(messages
[1],
495 sizeof (messages
[1]),
496 dgettext(TEXT_DOMAIN
,
497 "No resource pool accepting "
498 "default bindings exists"));
501 syslog(LOG_AUTH
| LOG_ERR
,
502 "pam_unix_cred: project \"%s\" could not "
503 "bind to resource pool: The resource pool "
504 "is unknown", proj
.pj_name
);
505 (void) snprintf(messages
[1],
506 sizeof (messages
[1]),
507 dgettext(TEXT_DOMAIN
,
508 "The specified resource pool "
512 (void) snprintf(messages
[1],
513 sizeof (messages
[1]),
514 dgettext(TEXT_DOMAIN
,
515 "Failure during pool binding"));
516 syslog(LOG_AUTH
| LOG_ERR
,
517 "pam_unix_cred: project \"%s\" could not "
518 "bind to resource pool: %m", proj
.pj_name
);
521 (void) __pam_display_msg(pamh
, PAM_ERROR_MSG
,
526 * Resource control assignment failed. Unlike
527 * newtask(1m), we treat this as an error.
531 * This isn't supposed to happen, but in
532 * case it does, this error message
533 * doesn't use error as an index, like
536 syslog(LOG_AUTH
| LOG_ERR
,
537 "pam_unix_cred: unkwown error joining "
538 "project \"%s\" (%d)", proj
.pj_name
, error
);
539 (void) snprintf(messages
[0],
540 sizeof (messages
[0]),
541 dgettext(TEXT_DOMAIN
,
542 "unkwown error joining project \"%s\""
543 " (%d)"), proj
.pj_name
, error
);
544 } else if ((kv_array
= _str2kva(proj
.pj_attr
, KV_ASSIGN
,
545 KV_DELIMITER
)) != NULL
) {
546 syslog(LOG_AUTH
| LOG_ERR
,
547 "pam_unix_cred: %s resource control "
548 "assignment failed for project \"%s\"",
549 kv_array
->data
[error
- 1].key
,
551 (void) snprintf(messages
[0],
552 sizeof (messages
[0]),
553 dgettext(TEXT_DOMAIN
,
554 "%s resource control assignment failed for "
556 kv_array
->data
[error
- 1].key
,
560 syslog(LOG_AUTH
| LOG_ERR
,
561 "pam_unix_cred: resource control "
562 "assignment failed for project \"%s\""
563 "attribute %d", proj
.pj_name
, error
);
564 (void) snprintf(messages
[0],
565 sizeof (messages
[0]),
566 dgettext(TEXT_DOMAIN
,
567 "resource control assignment failed for "
568 "project \"%s\" attribute %d"),
569 proj
.pj_name
, error
);
572 (void) __pam_display_msg(pamh
, PAM_ERROR_MSG
,
575 return (PAM_SYSTEM_ERR
);
578 tset
= def
= lim
= NULL
;
579 deflim
.def
= deflim
.lim
= NULL
;
581 (void) _enum_attrs(user
, finddeflim
, NULL
, &deflim
);
583 if (getset(deflim
.lim
, &lim
) != 0 || getset(deflim
.def
, &def
) != 0) {
584 ret
= PAM_SYSTEM_ERR
;
589 def
= priv_allocset();
591 ret
= PAM_SYSTEM_ERR
;
596 if ((pathconf("/", _PC_CHOWN_RESTRICTED
) == -1) && (errno
== 0))
597 (void) priv_addset(def
, PRIV_FILE_CHOWN_SELF
);
600 * Silently limit the privileges to those actually available
601 * in the current zone.
603 tset
= priv_allocset();
605 ret
= PAM_SYSTEM_ERR
;
608 if (getppriv(PRIV_PERMITTED
, tset
) != 0) {
609 ret
= PAM_SYSTEM_ERR
;
612 if (!priv_issubset(def
, tset
))
613 priv_intersect(tset
, def
);
615 * We set privilege awareness here so that I gets copied to
616 * P & E when the final setuid(uid) happens.
618 (void) setpflags(PRIV_AWARE
, 1);
619 if (setppriv(PRIV_SET
, PRIV_INHERITABLE
, def
) != 0) {
620 syslog(LOG_AUTH
| LOG_ERR
,
621 "pam_setcred: setppriv(defaultpriv) failed: %m");
627 * Silently limit the privileges to the limit set available.
629 if (getppriv(PRIV_LIMIT
, tset
) != 0) {
630 ret
= PAM_SYSTEM_ERR
;
633 if (!priv_issubset(lim
, tset
))
634 priv_intersect(tset
, lim
);
635 if (setppriv(PRIV_SET
, PRIV_LIMIT
, lim
) != 0) {
636 syslog(LOG_AUTH
| LOG_ERR
,
637 "pam_setcred: setppriv(limitpriv) failed: %m");
642 * In order not to surprise certain applications, we
643 * need to get rid of privilege awareness and thus we must
644 * set this flag which will cause a reset on set*uid().
646 (void) setpflags(PRIV_AWARE_RESET
, 1);
649 * This may fail but we do not care as this will be reset later
650 * when the uids are set to their final values.
652 (void) setpflags(PRIV_AWARE
, 0);
654 * Remove PRIV_PFEXEC; stop running as if we are under a profile
655 * shell. A user with a profile shell will set PRIV_PFEXEC.
657 (void) setpflags(PRIV_PFEXEC
, 0);