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>
42 #include <security/pam_appl.h>
43 #include <security/pam_modules.h>
44 #include <security/pam_impl.h>
46 #define PROJECT "project="
47 #define PROJSZ (sizeof (PROJECT) - 1)
50 * unix_cred - PAM auth modules must contain both pam_sm_authenticate
51 * and pam_sm_setcred. Some other auth module is responsible
52 * for authentication (e.g., pam_unix_auth.so), this module
53 * only implements pam_sm_setcred so that the authentication
54 * can be separated without knowledge of the Solaris Unix style
56 * Solaris Unix style credential setting includes
57 * setting the user's default and limit privileges.
61 * unix_cred - pam_sm_authenticate
68 pam_sm_authenticate(pam_handle_t
*pamh
, int flags
, int argc
, const char **argv
)
74 * Set the privilege set. The attributes are enumerated by _enum_attrs,
75 * including the attribues user_attr, prof_attr and policy.conf
78 getset(char *str
, priv_set_t
**res
)
87 len
= strlen(str
) + 1;
89 (void) memset(badp
, '\0', len
);
92 tmp
= priv_str_to_set(str
, ",", &endp
);
97 /* Now remove the bad privilege endp points to */
98 q
= strchr(endp
, ',');
100 q
= endp
+ strlen(endp
);
103 (void) strlcat(badp
, ",", len
);
104 /* Memset above guarantees NUL termination */
106 (void) strncat(badp
, endp
, q
- endp
);
107 /* excise bad privilege; strtok ignores 2x sep */
108 (void) memmove((void *)endp
, q
, strlen(q
) + 1);
110 } while (tmp
== NULL
&& *str
!= '\0');
113 syslog(LOG_AUTH
|LOG_ERR
,
114 "pam_setcred: can't parse privilege specification: %m\n");
116 } else if (*badp
!= '\0') {
117 syslog(LOG_AUTH
|LOG_DEBUG
,
118 "pam_setcred: unrecognized privilege(s): %s\n", badp
);
124 typedef struct deflim
{
131 finddeflim(const char *name
, kva_t
*kva
, void *ctxt
, void *pres
)
133 deflim_t
*pdef
= pres
;
136 if (pdef
->def
== NULL
) {
137 val
= kva_match(kva
, USERATTR_DFLTPRIV_KW
);
139 pdef
->def
= strdup(val
);
141 if (pdef
->lim
== NULL
) {
142 val
= kva_match(kva
, USERATTR_LIMPRIV_KW
);
144 pdef
->lim
= strdup(val
);
146 return (pdef
->lim
!= NULL
&& pdef
->def
!= NULL
);
150 * unix_cred - pam_sm_setcred
152 * Entry flags = PAM_ESTABLISH_CRED, set up Solaris Unix cred.
153 * PAM_DELETE_CRED, NOP, return PAM_SUCCESS.
154 * PAM_REINITIALIZE_CRED, set up Solaris Unix cred,
155 * or merge the current context with the new
157 * PAM_REFRESH_CRED, set up Solaris Unix cred.
158 * PAM_SILENT, print no messages to user.
160 * Returns PAM_SUCCESS, if all successful.
161 * PAM_CRED_ERR, if unable to set credentials.
162 * PAM_USER_UNKNOWN, if PAM_USER not set, or unable to find
164 * PAM_SYSTEM_ERR, if no valid flag.
168 pam_sm_setcred(pam_handle_t
*pamh
, int flags
, int argc
, const char **argv
)
172 uint_t nowarn
= flags
& PAM_SILENT
;
173 int ret
= PAM_SUCCESS
;
178 priv_set_t
*lim
, *def
, *tset
;
179 char messages
[PAM_MAX_NUM_MSG
][PAM_MAX_MSG_SIZE
];
180 char buf
[PROJECT_BUFSZ
];
181 struct project proj
, *pproj
;
187 char pwbuf
[NSS_BUFLEN_PASSWD
];
190 for (i
= 0; i
< argc
; i
++) {
191 if (strcmp(argv
[i
], "debug") == 0)
193 else if (strcmp(argv
[i
], "nowarn") == 0)
198 syslog(LOG_AUTH
| LOG_DEBUG
,
199 "pam_unix_cred: pam_sm_setcred(flags = %x, argc= %d)",
202 (void) pam_get_item(pamh
, PAM_USER
, (void **)&user
);
204 if (user
== NULL
|| *user
== '\0') {
205 syslog(LOG_AUTH
| LOG_ERR
,
206 "pam_unix_cred: USER NULL or empty!\n");
207 return (PAM_USER_UNKNOWN
);
209 (void) pam_get_item(pamh
, PAM_AUSER
, (void **)&auser
);
210 (void) pam_get_item(pamh
, PAM_RHOST
, (void **)&rhost
);
211 (void) pam_get_item(pamh
, PAM_TTY
, (void **)&tty
);
213 syslog(LOG_AUTH
| LOG_DEBUG
,
214 "pam_unix_cred: user = %s, auser = %s, rhost = %s, "
216 (auser
== NULL
) ? "NULL" : (*auser
== '\0') ? "ZERO" :
218 (rhost
== NULL
) ? "NULL" : (*rhost
== '\0') ? "ZERO" :
220 (tty
== NULL
) ? "NULL" : (*tty
== '\0') ? "ZERO" :
224 switch (flags
& (PAM_ESTABLISH_CRED
| PAM_DELETE_CRED
|
225 PAM_REINITIALIZE_CRED
| PAM_REFRESH_CRED
)) {
227 /* set default flag */
228 flags
|= PAM_ESTABLISH_CRED
;
230 case PAM_ESTABLISH_CRED
:
231 case PAM_REINITIALIZE_CRED
:
232 case PAM_REFRESH_CRED
:
234 case PAM_DELETE_CRED
:
235 return (PAM_SUCCESS
);
237 syslog(LOG_AUTH
| LOG_ERR
,
238 "pam_unix_cred: invalid flags %x", flags
);
239 return (PAM_SYSTEM_ERR
);
242 getpwnam_r(user
, &pwd
, pwbuf
, sizeof (pwbuf
), &pwdp
);
244 syslog(LOG_AUTH
| LOG_ERR
,
245 "pam_unix_cred: cannot get passwd entry for user = %s",
247 return (PAM_USER_UNKNOWN
);
250 /* Initialize the user's project */
251 (void) pam_get_item(pamh
, PAM_RESOURCE
, (void **)&kvs
);
253 char *tmp
, *lasts
, *tok
;
255 kvs
= tmp
= strdup(kvs
);
257 return (PAM_BUF_ERR
);
259 while ((tok
= strtok_r(tmp
, ";", &lasts
)) != NULL
) {
260 if (strncmp(tok
, PROJECT
, PROJSZ
) == 0) {
261 projname
= tok
+ PROJSZ
;
270 if (projname
== NULL
|| *projname
== '\0') {
271 pproj
= getdefaultproj(user
, &proj
, (void *)&buf
,
274 pproj
= getprojbyname(projname
, &proj
, (void *)&buf
,
277 /* projname points into kvs, so this is the first opportunity to free */
280 syslog(LOG_AUTH
| LOG_ERR
,
281 "pam_unix_cred: no default project for user %s", user
);
283 (void) snprintf(messages
[0], sizeof (messages
[0]),
284 dgettext(TEXT_DOMAIN
, "No default project!"));
285 (void) __pam_display_msg(pamh
, PAM_ERROR_MSG
,
288 return (PAM_SYSTEM_ERR
);
290 if ((error
= setproject(proj
.pj_name
, user
, TASK_NORMAL
)) != 0) {
294 case SETPROJ_ERR_TASK
:
295 if (errno
== EAGAIN
) {
296 syslog(LOG_AUTH
| LOG_ERR
,
297 "pam_unix_cred: project \"%s\" resource "
298 "control limit has been reached",
300 (void) snprintf(messages
[0],
301 sizeof (messages
[0]), dgettext(
303 "Resource control limit has been "
306 syslog(LOG_AUTH
| LOG_ERR
,
307 "pam_unix_cred: user %s could not join "
308 "project \"%s\": %m", user
, proj
.pj_name
);
309 (void) snprintf(messages
[0],
310 sizeof (messages
[0]), dgettext(
312 "Could not join default project"));
315 (void) __pam_display_msg(pamh
, PAM_ERROR_MSG
, 1,
318 case SETPROJ_ERR_POOL
:
319 (void) snprintf(messages
[0], sizeof (messages
[0]),
320 dgettext(TEXT_DOMAIN
,
321 "Could not bind to resource pool"));
324 syslog(LOG_AUTH
| LOG_ERR
,
325 "pam_unix_cred: project \"%s\" could not "
326 "bind to resource pool: No resource pool "
327 "accepting default bindings exists",
329 (void) snprintf(messages
[1],
330 sizeof (messages
[1]),
331 dgettext(TEXT_DOMAIN
,
332 "No resource pool accepting "
333 "default bindings exists"));
336 syslog(LOG_AUTH
| LOG_ERR
,
337 "pam_unix_cred: project \"%s\" could not "
338 "bind to resource pool: The resource pool "
339 "is unknown", proj
.pj_name
);
340 (void) snprintf(messages
[1],
341 sizeof (messages
[1]),
342 dgettext(TEXT_DOMAIN
,
343 "The specified resource pool "
347 (void) snprintf(messages
[1],
348 sizeof (messages
[1]),
349 dgettext(TEXT_DOMAIN
,
350 "Failure during pool binding"));
351 syslog(LOG_AUTH
| LOG_ERR
,
352 "pam_unix_cred: project \"%s\" could not "
353 "bind to resource pool: %m", proj
.pj_name
);
356 (void) __pam_display_msg(pamh
, PAM_ERROR_MSG
,
361 * Resource control assignment failed. Unlike
362 * newtask(1m), we treat this as an error.
366 * This isn't supposed to happen, but in
367 * case it does, this error message
368 * doesn't use error as an index, like
371 syslog(LOG_AUTH
| LOG_ERR
,
372 "pam_unix_cred: unkwown error joining "
373 "project \"%s\" (%d)", proj
.pj_name
, error
);
374 (void) snprintf(messages
[0],
375 sizeof (messages
[0]),
376 dgettext(TEXT_DOMAIN
,
377 "unkwown error joining project \"%s\""
378 " (%d)"), proj
.pj_name
, error
);
379 } else if ((kv_array
= _str2kva(proj
.pj_attr
, KV_ASSIGN
,
380 KV_DELIMITER
)) != NULL
) {
381 syslog(LOG_AUTH
| LOG_ERR
,
382 "pam_unix_cred: %s resource control "
383 "assignment failed for project \"%s\"",
384 kv_array
->data
[error
- 1].key
,
386 (void) snprintf(messages
[0],
387 sizeof (messages
[0]),
388 dgettext(TEXT_DOMAIN
,
389 "%s resource control assignment failed for "
391 kv_array
->data
[error
- 1].key
,
395 syslog(LOG_AUTH
| LOG_ERR
,
396 "pam_unix_cred: resource control "
397 "assignment failed for project \"%s\""
398 "attribute %d", proj
.pj_name
, error
);
399 (void) snprintf(messages
[0],
400 sizeof (messages
[0]),
401 dgettext(TEXT_DOMAIN
,
402 "resource control assignment failed for "
403 "project \"%s\" attribute %d"),
404 proj
.pj_name
, error
);
407 (void) __pam_display_msg(pamh
, PAM_ERROR_MSG
,
410 return (PAM_SYSTEM_ERR
);
413 tset
= def
= lim
= NULL
;
414 deflim
.def
= deflim
.lim
= NULL
;
416 (void) _enum_attrs(user
, finddeflim
, NULL
, &deflim
);
418 if (getset(deflim
.lim
, &lim
) != 0 || getset(deflim
.def
, &def
) != 0) {
419 ret
= PAM_SYSTEM_ERR
;
424 def
= priv_allocset();
426 ret
= PAM_SYSTEM_ERR
;
431 if ((pathconf("/", _PC_CHOWN_RESTRICTED
) == -1) && (errno
== 0))
432 (void) priv_addset(def
, PRIV_FILE_CHOWN_SELF
);
435 * Silently limit the privileges to those actually available
436 * in the current zone.
438 tset
= priv_allocset();
440 ret
= PAM_SYSTEM_ERR
;
443 if (getppriv(PRIV_PERMITTED
, tset
) != 0) {
444 ret
= PAM_SYSTEM_ERR
;
447 if (!priv_issubset(def
, tset
))
448 priv_intersect(tset
, def
);
450 * We set privilege awareness here so that I gets copied to
451 * P & E when the final setuid(uid) happens.
453 (void) setpflags(PRIV_AWARE
, 1);
454 if (setppriv(PRIV_SET
, PRIV_INHERITABLE
, def
) != 0) {
455 syslog(LOG_AUTH
| LOG_ERR
,
456 "pam_setcred: setppriv(defaultpriv) failed: %m");
462 * Silently limit the privileges to the limit set available.
464 if (getppriv(PRIV_LIMIT
, tset
) != 0) {
465 ret
= PAM_SYSTEM_ERR
;
468 if (!priv_issubset(lim
, tset
))
469 priv_intersect(tset
, lim
);
470 if (setppriv(PRIV_SET
, PRIV_LIMIT
, lim
) != 0) {
471 syslog(LOG_AUTH
| LOG_ERR
,
472 "pam_setcred: setppriv(limitpriv) failed: %m");
477 * In order not to surprise certain applications, we
478 * need to get rid of privilege awareness and thus we must
479 * set this flag which will cause a reset on set*uid().
481 (void) setpflags(PRIV_AWARE_RESET
, 1);
484 * This may fail but we do not care as this will be reset later
485 * when the uids are set to their final values.
487 (void) setpflags(PRIV_AWARE
, 0);
489 * Remove PRIV_PFEXEC; stop running as if we are under a profile
490 * shell. A user with a profile shell will set PRIV_PFEXEC.
492 (void) setpflags(PRIV_PFEXEC
, 0);