diff options
author | Tomas Mraz <tm@t8m.info> | 2007-06-15 09:38:11 +0000 |
---|---|---|
committer | Tomas Mraz <tm@t8m.info> | 2007-06-15 09:38:11 +0000 |
commit | b3644707da87d61559f8322771a88d2162a47a4e (patch) | |
tree | 620084823d93226cdd9ea69cd375f1d7372c67f4 /modules/pam_namespace/pam_namespace.c | |
parent | 7b33ec050402db86919da97c720dacaf280681e6 (diff) | |
download | pam-b3644707da87d61559f8322771a88d2162a47a4e.tar.gz pam-b3644707da87d61559f8322771a88d2162a47a4e.tar.bz2 pam-b3644707da87d61559f8322771a88d2162a47a4e.zip |
Relevant BUGIDs:
Purpose of commit: bugfix, new feature
Commit summary:
---------------
2007-06-15 Tomas Mraz <t8m@centrum.cz>
* modules/pam_namespace/README.xml: Avoid duplication of
documentation.
* modules/pam_namespace/namespace.conf: More real life example
from MLS support.
* modules/pam_namespace/namespace.conf.5.xml: Likewise plus
properly describe how instance directory names are formed.
* modules/pam_namespace/namespace.init: Preserve euid when
called from setuid apps (su, newrole).
* modules/pam_namespace/pam_namespace.8.xml: Added option
no_unmount_on_close.
* modules/pam_namespace/pam_namespace.c (process_line): Polyinst
methods are now user, level and context. Fix crash on unknown
override user in config file.
(ns_override): Add explicit uid parameter.
(form_context): Skip for user method. Implement level based
polyinstantiation.
(poly_name): Initialize contexts. Add level based polyinst,
remove 'both' metod. Use raw contexts for instance names,
truncate long instance names and add hash.
(ns_setup): Hashing moved to poly_name().
(setup_namespace): Handle correctly override users for
su (when unmnt_remnt is used).
(pam_sm_close_session): Added no_unmount_on_close option.
* modules/pam_namespace/pam_namespace.h: Added
no_unmount_on_close_option, level method, limit on instance
directory name length.
Diffstat (limited to 'modules/pam_namespace/pam_namespace.c')
-rw-r--r-- | modules/pam_namespace/pam_namespace.c | 313 |
1 files changed, 224 insertions, 89 deletions
diff --git a/modules/pam_namespace/pam_namespace.c b/modules/pam_namespace/pam_namespace.c index e4e4a5d8..73d8e591 100644 --- a/modules/pam_namespace/pam_namespace.c +++ b/modules/pam_namespace/pam_namespace.c @@ -244,23 +244,29 @@ static int process_line(char *line, const char *home, } strcpy(poly.dir, dir); strcpy(poly.instance_prefix, instance_prefix); - if (strcmp(method, "user") == 0) - poly.method = USER; + + poly.method = NONE; + if (strcmp(method, "user") == 0) + poly.method = USER; + #ifdef WITH_SELINUX - else if (strcmp(method, "context") == 0) { + if (strcmp(method, "level") == 0) { if (idata->flags & PAMNS_CTXT_BASED_INST) - poly.method = CONTEXT; + poly.method = LEVEL; else poly.method = USER; - } else if (strcmp(method, "both") == 0) { + } + + if (strcmp(method, "context") == 0) { if (idata->flags & PAMNS_CTXT_BASED_INST) - poly.method = BOTH; + poly.method = CONTEXT; else poly.method = USER; } #endif - else { + + if ( poly.method == NONE) { pam_syslog(idata->pamh, LOG_NOTICE, "Illegal method"); goto skipping; } @@ -296,11 +302,14 @@ static int process_line(char *line, const char *home, *tptr = '\0'; pwd = pam_modutil_getpwnam(idata->pamh, ustr); - *uidptr = pwd->pw_uid; - if (i < count - 1) { - ustr = tptr + 1; + if (pwd == NULL) { + pam_syslog(idata->pamh, LOG_ERR, "Unknown user %s in configuration", ustr); + poly.num_uids--; + } else { + *uidptr = pwd->pw_uid; uidptr++; } + ustr = tptr + 1; } } @@ -411,17 +420,18 @@ static int parse_config_file(struct instance_data *idata) * uids for the polyinstantiated directory, polyinstantiation is not * performed for that user for that directory. */ -static int ns_override(struct polydir_s *polyptr, struct instance_data *idata) +static int ns_override(struct polydir_s *polyptr, struct instance_data *idata, + uid_t uid) { unsigned int i; if (idata->flags & PAMNS_DEBUG) pam_syslog(idata->pamh, LOG_DEBUG, "Checking for ns override in dir %s for uid %d", - polyptr->dir, idata->uid); + polyptr->dir, uid); for (i = 0; i < polyptr->num_uids; i++) { - if (idata->uid == polyptr->uid[i]) { + if (uid == polyptr->uid[i]) { return 1; } } @@ -429,6 +439,36 @@ static int ns_override(struct polydir_s *polyptr, struct instance_data *idata) return 0; } +/* + * md5hash generates a hash of the passed in instance directory name. + */ +static char *md5hash(const char *instname, struct instance_data *idata) +{ + int i; + char *md5inst = NULL; + char *to; + unsigned char inst_digest[MD5_DIGEST_LENGTH]; + + /* + * Create MD5 hashes for instance pathname. + */ + + MD5((const unsigned char *)instname, strlen(instname), inst_digest); + + if ((md5inst = malloc(MD5_DIGEST_LENGTH * 2 + 1)) == NULL) { + pam_syslog(idata->pamh, LOG_ERR, "Unable to allocate buffer"); + return NULL; + } + + to = md5inst; + for (i = 0; i < MD5_DIGEST_LENGTH; i++) { + snprintf(to, 3, "%02x", (unsigned int)inst_digest[i]); + to += 2; + } + + return md5inst; +} + #ifdef WITH_SELINUX static int form_context(const struct polydir_s *polyptr, security_context_t *i_context, security_context_t *origcon, @@ -448,19 +488,23 @@ static int form_context(const struct polydir_s *polyptr, return PAM_SESSION_ERR; } + if (polyptr->method == USER) return PAM_SUCCESS; + + rc = getexeccon(&scon); + if (rc < 0 || scon == NULL) { + pam_syslog(idata->pamh, LOG_ERR, + "Error getting exec context, %m"); + return PAM_SESSION_ERR; + } + /* * If polyinstantiating based on security context, get current * process security context, get security class for directories, * and ask the policy to provide security context of the * polyinstantiated instance directory. */ - if ((polyptr->method == CONTEXT) || (polyptr->method == BOTH)) { - rc = getexeccon(&scon); - if (rc < 0 || scon == NULL) { - pam_syslog(idata->pamh, LOG_ERR, - "Error getting exec context, %m"); - return PAM_SESSION_ERR; - } + + if (polyptr->method == CONTEXT) { tclass = string_to_security_class("dir"); if (security_compute_member(scon, *origcon, tclass, @@ -473,7 +517,48 @@ static int form_context(const struct polydir_s *polyptr, pam_syslog(idata->pamh, LOG_DEBUG, "member context returned by policy %s", *i_context); freecon(scon); + return PAM_SUCCESS; } + + /* + * If polyinstantiating based on security level, get current + * process security context, get security class for directories, + * and change the directories MLS Level to match process. + */ + + if (polyptr->method == LEVEL) { + context_t scontext = NULL; + context_t fcontext = NULL; + rc = PAM_SESSION_ERR; + + scontext = context_new(scon); + if (! scontext) { + pam_syslog(idata->pamh, LOG_ERR, "out of memory"); + goto fail; + } + fcontext = context_new(*origcon); + if (! fcontext) { + pam_syslog(idata->pamh, LOG_ERR, "out of memory"); + goto fail; + } + if (context_range_set(fcontext, context_range_get(scontext)) != 0) { + pam_syslog(idata->pamh, LOG_ERR, "Unable to set MLS Componant of context"); + goto fail; + } + *i_context=strdup(context_str(fcontext)); + if (! *i_context) { + pam_syslog(idata->pamh, LOG_ERR, "out of memory"); + goto fail; + } + + rc = PAM_SUCCESS; + fail: + context_free(scontext); + context_free(fcontext); + freecon(scon); + return rc; + } + /* Should never get here */ return PAM_SUCCESS; } #endif @@ -495,12 +580,21 @@ static int poly_name(const struct polydir_s *polyptr, char **i_name, #endif { int rc; + char *hash = NULL; +#ifdef WITH_SELINUX + security_context_t rawcon = NULL; +#endif -# ifdef WITH_SELINUX - rc = form_context(polyptr, i_context, origcon, idata); + *i_name = NULL; +#ifdef WITH_SELINUX + *i_context = NULL; + *origcon = NULL; + if ((rc=form_context(polyptr, i_context, origcon, idata)) != PAM_SUCCESS) { + return rc; + } #endif - rc = PAM_SUCCESS; + rc = PAM_SESSION_ERR; /* * Set the name of the polyinstantiated instance dir based on the * polyinstantiation method. @@ -509,35 +603,70 @@ static int poly_name(const struct polydir_s *polyptr, char **i_name, case USER: if (asprintf(i_name, "%s", idata->user) < 0) { *i_name = NULL; - rc = PAM_SESSION_ERR; - } + goto fail; + } break; #ifdef WITH_SELINUX + case LEVEL: case CONTEXT: - if (asprintf(i_name, "%s", *i_context) < 0) { + if (selinux_trans_to_raw_context(*i_context, &rawcon) < 0) { + pam_syslog(idata->pamh, LOG_ERR, "Error translating directory context"); + goto fail; + } + if (asprintf(i_name, "%s_%s", rawcon, idata->user) < 0) { *i_name = NULL; - rc = PAM_SESSION_ERR; + goto fail; } break; - case BOTH: - if (asprintf(i_name, "%s_%s", *i_context, idata->user) < 0) { - *i_name = NULL; - rc = PAM_SESSION_ERR; - } - break; #endif /* WITH_SELINUX */ default: if (idata->flags & PAMNS_DEBUG) pam_syslog(idata->pamh, LOG_ERR, "Unknown method"); - rc = PAM_SESSION_ERR; + goto fail; } - if ((idata->flags & PAMNS_DEBUG) && rc == PAM_SUCCESS) + if (idata->flags & PAMNS_DEBUG) pam_syslog(idata->pamh, LOG_DEBUG, "poly_name %s", *i_name); + if ((idata->flags & PAMNS_GEN_HASH) || strlen(*i_name) > NAMESPACE_MAX_DIR_LEN) { + hash = md5hash(*i_name, idata); + if (hash == NULL) { + goto fail; + } + if (idata->flags & PAMNS_GEN_HASH) { + free(*i_name); + *i_name = hash; + hash = NULL; + } else { + char *newname; + if (asprintf(&newname, "%.*s_%s", NAMESPACE_MAX_DIR_LEN-1-strlen(hash), + *i_name, hash) < 0) { + goto fail; + } + free(*i_name); + *i_name = newname; + } + } + rc = PAM_SUCCESS; + +fail: + free(hash); +#ifdef WITH_SELINUX + freecon(rawcon); +#endif + if (rc != PAM_SUCCESS) { +#ifdef WITH_SELINUX + freecon(*i_context); + *i_context = NULL; + freecon(*origcon); + *origcon = NULL; +#endif + free(*i_name); + *i_name = NULL; + } return rc; } @@ -785,39 +914,6 @@ inst_init: /* - * md5hash generates a hash of the passed in instance directory name. - */ -static int md5hash(char **instname, struct instance_data *idata) -{ - int i; - char *md5inst = NULL; - char *to; - unsigned char inst_digest[MD5_DIGEST_LENGTH]; - - /* - * Create MD5 hashes for instance pathname. - */ - - MD5((unsigned char *)*instname, strlen(*instname), inst_digest); - - if ((md5inst = malloc(MD5_DIGEST_LENGTH * 2 + 1)) == NULL) { - pam_syslog(idata->pamh, LOG_ERR, "Unable to allocate buffer"); - return PAM_SESSION_ERR; - } - - to = md5inst; - for (i = 0; i < MD5_DIGEST_LENGTH; i++) { - snprintf(to, 3, "%02x", (unsigned int)inst_digest[i]); - to += 3; - } - - free(*instname); - *instname = md5inst; - - return PAM_SUCCESS; -} - -/* * This function performs the namespace setup for a particular directory * that is being polyinstantiated. It creates an MD5 hash of instance * directory, calls create_dirs to create it with appropriate @@ -867,14 +963,6 @@ static int ns_setup(const struct polydir_s *polyptr, #endif } - if (idata->flags & PAMNS_GEN_HASH) { - retval = md5hash(&instname, idata); - if (retval < 0) { - pam_syslog(idata->pamh, LOG_ERR, "Error generating md5 hash"); - goto error_out; - } - } - if (asprintf(&inst_dir, "%s%s", polyptr->instance_prefix, instname) < 0) goto error_out; @@ -967,21 +1055,46 @@ static int setup_namespace(struct instance_data *idata, enum unmnt_op unmnt) int retval = 0, need_poly = 0, changing_dir = 0; char *cptr, *fptr, poly_parent[PATH_MAX]; struct polydir_s *pptr; + uid_t req_uid; + const void *ruser_name; + struct passwd *pwd; if (idata->flags & PAMNS_DEBUG) pam_syslog(idata->pamh, LOG_DEBUG, "Set up namespace for pid %d", getpid()); + retval = pam_get_item(idata->pamh, PAM_RUSER, &ruser_name); + if (ruser_name == NULL || retval != PAM_SUCCESS) { + retval = PAM_SUCCESS; + req_uid = getuid(); + } else { + pwd = pam_modutil_getpwnam(idata->pamh, ruser_name); + if (pwd != NULL) { + req_uid = pwd->pw_uid; + } else { + req_uid = getuid(); + } + } + /* * Cycle through all polyinstantiated directory entries to see if * polyinstantiation is needed at all. */ for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) { - if (ns_override(pptr, idata)) { - if (idata->flags & PAMNS_DEBUG) - pam_syslog(idata->pamh, LOG_DEBUG, + if (ns_override(pptr, idata, idata->uid)) { + if (unmnt == NO_UNMNT || ns_override(pptr, idata, req_uid)) { + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, "Overriding poly for user %d for dir %s", idata->uid, pptr->dir); + } else { + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, + "Need unmount ns for user %d for dir %s", + idata->uid, pptr->dir); + need_poly = 1; + break; + } continue; } else { if (idata->flags & PAMNS_DEBUG) @@ -1011,15 +1124,20 @@ static int setup_namespace(struct instance_data *idata, enum unmnt_op unmnt) * call ns_setup to setup polyinstantiation for a particular entry. */ for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) { - if (ns_override(pptr, idata)) - continue; - else { - if (idata->flags & PAMNS_DEBUG) + enum unmnt_op dir_unmnt = unmnt; + if (ns_override(pptr, idata, idata->uid)) { + if (unmnt == NO_UNMNT || ns_override(pptr, idata, req_uid)) { + continue; + } else { + dir_unmnt = UNMNT_ONLY; + } + } + if (idata->flags & PAMNS_DEBUG) pam_syslog(idata->pamh, LOG_DEBUG, "Setting poly ns for user %d for dir %s", idata->uid, pptr->dir); - if ((unmnt == UNMNT_REMNT) || (unmnt == UNMNT_ONLY)) { + if ((dir_unmnt == UNMNT_REMNT) || (dir_unmnt == UNMNT_ONLY)) { /* * Check to see if process current directory is in the * bind mounted instance_parent directory that we are trying to @@ -1059,13 +1177,12 @@ static int setup_namespace(struct instance_data *idata, enum unmnt_op unmnt) } else if (idata->flags & PAMNS_DEBUG) pam_syslog(idata->pamh, LOG_DEBUG, "Umount succeeded %s", pptr->dir); - } + } - if (unmnt != UNMNT_ONLY) { + if (dir_unmnt != UNMNT_ONLY) { retval = ns_setup(pptr, idata); if (retval != PAM_SUCCESS) break; - } } } @@ -1092,7 +1209,7 @@ static int orig_namespace(struct instance_data *idata) * appropriate polyinstantiated instance directories. */ for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) { - if (ns_override(pptr, idata)) + if (ns_override(pptr, idata, idata->uid)) continue; else { if (idata->flags & PAMNS_DEBUG) @@ -1158,7 +1275,7 @@ PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED, #ifdef WITH_SELINUX if (is_selinux_enabled()) idata.flags |= PAMNS_SELINUX_ENABLED; - if (ctxt_based_inst_needed()) + if (ctxt_based_inst_needed()) idata.flags |= PAMNS_CTXT_BASED_INST; #endif @@ -1266,12 +1383,30 @@ PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED, idata.flags |= PAMNS_DEBUG; if (strcmp(argv[i], "ignore_config_error") == 0) idata.flags |= PAMNS_IGN_CONFIG_ERR; + if (strcmp(argv[i], "no_unmount_on_close") == 0) + idata.flags |= PAMNS_NO_UNMOUNT_ON_CLOSE; } if (idata.flags & PAMNS_DEBUG) pam_syslog(idata.pamh, LOG_DEBUG, "close_session - start"); /* + * For certain trusted programs such as newrole, open session + * is called from a child process while the parent perfoms + * close session and pam end functions. For these commands + * pam_close_session should not perform the unmount of the + * polyinstantiatied directory because it will result in + * undoing of parents polyinstantiatiaion. These commands + * will invoke pam_namespace with the "no_unmount_on_close" + * argument. + */ + if (idata.flags & PAMNS_NO_UNMOUNT_ON_CLOSE) { + if (idata.flags & PAMNS_DEBUG) + pam_syslog(idata.pamh, LOG_DEBUG, "close_session - sucessful"); + return PAM_SUCCESS; + } + + /* * Lookup user and fill struct items */ retval = pam_get_item(idata.pamh, PAM_USER, (void*) &user_name ); |