Merge remote-tracking branch 'origin/master'
[unleashed/lotheac.git] / usr / src / lib / pam_modules / unix_cred / unix_cred.c
blobb7f8b32f52e1c2680fd4a117f7f256268bfa2df9
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
22 * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
25 #include <nss_dbdefs.h>
26 #include <pwd.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <syslog.h>
30 #include <unistd.h>
31 #include <auth_attr.h>
32 #include <deflt.h>
33 #include <priv.h>
34 #include <secdb.h>
35 #include <user_attr.h>
36 #include <sys/task.h>
37 #include <libintl.h>
38 #include <project.h>
39 #include <errno.h>
40 #include <alloca.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
55 * credential setting.
56 * Solaris Unix style credential setting includes
57 * setting the user's default and limit privileges.
61 * unix_cred - pam_sm_authenticate
63 * Returns PAM_IGNORE.
66 /*ARGSUSED*/
67 int
68 pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
70 return (PAM_IGNORE);
74 * Set the privilege set. The attributes are enumerated by _enum_attrs,
75 * including the attribues user_attr, prof_attr and policy.conf
77 static int
78 getset(char *str, priv_set_t **res)
80 priv_set_t *tmp;
81 char *badp;
82 int len;
84 if (str == NULL)
85 return (0);
87 len = strlen(str) + 1;
88 badp = alloca(len);
89 (void) memset(badp, '\0', len);
90 do {
91 const char *q, *endp;
92 tmp = priv_str_to_set(str, ",", &endp);
93 if (tmp == NULL) {
94 if (endp == NULL)
95 break;
97 /* Now remove the bad privilege endp points to */
98 q = strchr(endp, ',');
99 if (q == NULL)
100 q = endp + strlen(endp);
102 if (*badp != '\0')
103 (void) strlcat(badp, ",", len);
104 /* Memset above guarantees NUL termination */
105 /* LINTED */
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');
112 if (tmp == NULL) {
113 syslog(LOG_AUTH|LOG_ERR,
114 "pam_setcred: can't parse privilege specification: %m\n");
115 return (-1);
116 } else if (*badp != '\0') {
117 syslog(LOG_AUTH|LOG_DEBUG,
118 "pam_setcred: unrecognized privilege(s): %s\n", badp);
120 *res = tmp;
121 return (0);
124 typedef struct deflim {
125 char *def;
126 char *lim;
127 } deflim_t;
129 /*ARGSUSED*/
130 static int
131 finddeflim(const char *name, kva_t *kva, void *ctxt, void *pres)
133 deflim_t *pdef = pres;
134 char *val;
136 if (pdef->def == NULL) {
137 val = kva_match(kva, USERATTR_DFLTPRIV_KW);
138 if (val != NULL)
139 pdef->def = strdup(val);
141 if (pdef->lim == NULL) {
142 val = kva_match(kva, USERATTR_LIMPRIV_KW);
143 if (val != NULL)
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
156 * user.
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
163 * user in databases.
164 * PAM_SYSTEM_ERR, if no valid flag.
168 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
170 int i;
171 int debug = 0;
172 uint_t nowarn = flags & PAM_SILENT;
173 int ret = PAM_SUCCESS;
174 char *user;
175 char *auser;
176 char *rhost;
177 char *tty;
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;
182 int error;
183 char *projname;
184 char *kvs;
185 struct passwd pwd;
186 struct passwd *pwdp;
187 char pwbuf[NSS_BUFLEN_PASSWD];
188 deflim_t deflim;
190 for (i = 0; i < argc; i++) {
191 if (strcmp(argv[i], "debug") == 0)
192 debug = 1;
193 else if (strcmp(argv[i], "nowarn") == 0)
194 nowarn |= 1;
197 if (debug)
198 syslog(LOG_AUTH | LOG_DEBUG,
199 "pam_unix_cred: pam_sm_setcred(flags = %x, argc= %d)",
200 flags, argc);
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);
212 if (debug)
213 syslog(LOG_AUTH | LOG_DEBUG,
214 "pam_unix_cred: user = %s, auser = %s, rhost = %s, "
215 "tty = %s", user,
216 (auser == NULL) ? "NULL" : (*auser == '\0') ? "ZERO" :
217 auser,
218 (rhost == NULL) ? "NULL" : (*rhost == '\0') ? "ZERO" :
219 rhost,
220 (tty == NULL) ? "NULL" : (*tty == '\0') ? "ZERO" :
221 tty);
223 /* validate flags */
224 switch (flags & (PAM_ESTABLISH_CRED | PAM_DELETE_CRED |
225 PAM_REINITIALIZE_CRED | PAM_REFRESH_CRED)) {
226 case 0:
227 /* set default flag */
228 flags |= PAM_ESTABLISH_CRED;
229 break;
230 case PAM_ESTABLISH_CRED:
231 case PAM_REINITIALIZE_CRED:
232 case PAM_REFRESH_CRED:
233 break;
234 case PAM_DELETE_CRED:
235 return (PAM_SUCCESS);
236 default:
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);
243 if (!pwdp) {
244 syslog(LOG_AUTH | LOG_ERR,
245 "pam_unix_cred: cannot get passwd entry for user = %s",
246 user);
247 return (PAM_USER_UNKNOWN);
250 /* Initialize the user's project */
251 (void) pam_get_item(pamh, PAM_RESOURCE, (void **)&kvs);
252 if (kvs != NULL) {
253 char *tmp, *lasts, *tok;
255 kvs = tmp = strdup(kvs);
256 if (kvs == NULL)
257 return (PAM_BUF_ERR);
259 while ((tok = strtok_r(tmp, ";", &lasts)) != NULL) {
260 if (strncmp(tok, PROJECT, PROJSZ) == 0) {
261 projname = tok + PROJSZ;
262 break;
264 tmp = NULL;
266 } else {
267 projname = NULL;
270 if (projname == NULL || *projname == '\0') {
271 pproj = getdefaultproj(user, &proj, (void *)&buf,
272 PROJECT_BUFSZ);
273 } else {
274 pproj = getprojbyname(projname, &proj, (void *)&buf,
275 PROJECT_BUFSZ);
277 /* projname points into kvs, so this is the first opportunity to free */
278 free(kvs);
279 if (pproj == NULL) {
280 syslog(LOG_AUTH | LOG_ERR,
281 "pam_unix_cred: no default project for user %s", user);
282 if (!nowarn) {
283 (void) snprintf(messages[0], sizeof (messages[0]),
284 dgettext(TEXT_DOMAIN, "No default project!"));
285 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
286 1, messages, NULL);
288 return (PAM_SYSTEM_ERR);
290 if ((error = setproject(proj.pj_name, user, TASK_NORMAL)) != 0) {
291 kva_t *kv_array;
293 switch (error) {
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",
299 proj.pj_name);
300 (void) snprintf(messages[0],
301 sizeof (messages[0]), dgettext(
302 TEXT_DOMAIN,
303 "Resource control limit has been "
304 "reached"));
305 } else {
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(
311 TEXT_DOMAIN,
312 "Could not join default project"));
314 if (!nowarn)
315 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1,
316 messages, NULL);
317 break;
318 case SETPROJ_ERR_POOL:
319 (void) snprintf(messages[0], sizeof (messages[0]),
320 dgettext(TEXT_DOMAIN,
321 "Could not bind to resource pool"));
322 switch (errno) {
323 case EACCES:
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",
328 proj.pj_name);
329 (void) snprintf(messages[1],
330 sizeof (messages[1]),
331 dgettext(TEXT_DOMAIN,
332 "No resource pool accepting "
333 "default bindings exists"));
334 break;
335 case ESRCH:
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 "
344 "is unknown"));
345 break;
346 default:
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);
355 if (!nowarn)
356 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
357 2, messages, NULL);
358 break;
359 default:
361 * Resource control assignment failed. Unlike
362 * newtask(1m), we treat this as an error.
364 if (error < 0) {
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
369 * the others might.
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,
385 proj.pj_name);
386 (void) snprintf(messages[0],
387 sizeof (messages[0]),
388 dgettext(TEXT_DOMAIN,
389 "%s resource control assignment failed for "
390 "project \"%s\""),
391 kv_array->data[error - 1].key,
392 proj.pj_name);
393 _kva_free(kv_array);
394 } else {
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);
406 if (!nowarn)
407 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
408 1, messages, NULL);
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;
420 goto out;
423 if (def == NULL) {
424 def = priv_allocset();
425 if (def == NULL) {
426 ret = PAM_SYSTEM_ERR;
427 goto out;
429 priv_basicset(def);
430 errno = 0;
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();
439 if (tset == NULL) {
440 ret = PAM_SYSTEM_ERR;
441 goto out;
443 if (getppriv(PRIV_PERMITTED, tset) != 0) {
444 ret = PAM_SYSTEM_ERR;
445 goto out;
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");
457 ret = PAM_CRED_ERR;
460 if (lim != NULL) {
462 * Silently limit the privileges to the limit set available.
464 if (getppriv(PRIV_LIMIT, tset) != 0) {
465 ret = PAM_SYSTEM_ERR;
466 goto out;
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");
473 ret = PAM_CRED_ERR;
474 goto out;
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);
494 out:
495 free(deflim.lim);
496 free(deflim.def);
498 if (lim != NULL)
499 priv_freeset(lim);
500 if (def != NULL)
501 priv_freeset(def);
502 if (tset != NULL)
503 priv_freeset(tset);
505 return (ret);