blob: c5ea436ca5202741b630cd125032a4894ee8cc6b [file] [log] [blame]
/* Author: Mark Goldman <mgoldman@tresys.com>
* Paul Rosenfeld <prosenfeld@tresys.com>
* Todd C. Miller <tmiller@tresys.com>
*
* Copyright (C) 2007 Tresys Technology, LLC
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <semanage/handle.h>
#include <semanage/seusers_policy.h>
#include <semanage/users_policy.h>
#include <semanage/user_record.h>
#include <semanage/fcontext_record.h>
#include <semanage/fcontexts_policy.h>
#include <sepol/context.h>
#include <sepol/context_record.h>
#include "semanage_store.h"
#include "seuser_internal.h"
#include "debug.h"
#include "utilities.h"
#include "genhomedircon.h"
#include <ustr.h>
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pwd.h>
#include <errno.h>
#include <unistd.h>
#include <regex.h>
/* paths used in get_home_dirs() */
#define PATH_ETC_USERADD "/etc/default/useradd"
#define PATH_ETC_LIBUSER "/etc/libuser.conf"
#define PATH_DEFAULT_HOME "/home"
#define PATH_EXPORT_HOME "/export/home"
#define PATH_ETC_LOGIN_DEFS "/etc/login.defs"
/* other paths */
#define PATH_SHELLS_FILE "/etc/shells"
#define PATH_NOLOGIN_SHELL "/sbin/nologin"
/* comments written to context file */
#define COMMENT_FILE_CONTEXT_HEADER "#\n#\n# " \
"User-specific file contexts, generated via libsemanage\n" \
"# use semanage command to manage system users to change" \
" the file_context\n#\n#\n"
#define COMMENT_USER_HOME_CONTEXT "\n\n#\n# Home Context for user %s" \
"\n#\n\n"
/* placeholders used in the template file
which are searched for and replaced */
#define TEMPLATE_HOME_ROOT "HOME_ROOT"
#define TEMPLATE_HOME_DIR "HOME_DIR"
/* these are legacy */
#define TEMPLATE_USER "USER"
#define TEMPLATE_ROLE "ROLE"
/* new names */
#define TEMPLATE_USERNAME "%{USERNAME}"
#define TEMPLATE_USERID "%{USERID}"
#define TEMPLATE_SEUSER "system_u"
#define TEMPLATE_LEVEL "s0"
#define FALLBACK_SENAME "user_u"
#define FALLBACK_PREFIX "user"
#define FALLBACK_LEVEL "s0"
#define FALLBACK_NAME "[^/]+"
#define FALLBACK_UIDGID "[0-9]+"
#define DEFAULT_LOGIN "__default__"
typedef struct user_entry {
char *name;
char *uid;
char *gid;
char *sename;
char *prefix;
char *home;
char *level;
struct user_entry *next;
} genhomedircon_user_entry_t;
typedef struct {
const char *fcfilepath;
int usepasswd;
const char *homedir_template_path;
genhomedircon_user_entry_t *fallback;
semanage_handle_t *h_semanage;
sepol_policydb_t *policydb;
} genhomedircon_settings_t;
typedef struct {
const char *search_for;
const char *replace_with;
} replacement_pair_t;
typedef struct {
const char *dir;
int matched;
} fc_match_handle_t;
typedef struct IgnoreDir {
struct IgnoreDir *next;
char *dir;
} ignoredir_t;
ignoredir_t *ignore_head = NULL;
static void ignore_free(void) {
ignoredir_t *next;
while (ignore_head) {
next = ignore_head->next;
free(ignore_head->dir);
free(ignore_head);
ignore_head = next;
}
}
static int ignore_setup(char *ignoredirs) {
char *tok;
ignoredir_t *ptr = NULL;
tok = strtok(ignoredirs, ";");
while(tok) {
ptr = calloc(sizeof(ignoredir_t),1);
if (!ptr)
goto err;
ptr->dir = strdup(tok);
if (!ptr->dir)
goto err;
ptr->next = ignore_head;
ignore_head = ptr;
tok = strtok(NULL, ";");
}
return 0;
err:
free(ptr);
ignore_free();
return -1;
}
static int ignore(const char *homedir) {
ignoredir_t *ptr = ignore_head;
while (ptr) {
if (strcmp(ptr->dir, homedir) == 0) {
return 1;
}
ptr = ptr->next;
}
return 0;
}
static semanage_list_t *default_shell_list(void)
{
semanage_list_t *list = NULL;
if (semanage_list_push(&list, "/bin/csh")
|| semanage_list_push(&list, "/bin/tcsh")
|| semanage_list_push(&list, "/bin/ksh")
|| semanage_list_push(&list, "/bin/bsh")
|| semanage_list_push(&list, "/bin/ash")
|| semanage_list_push(&list, "/usr/bin/ksh")
|| semanage_list_push(&list, "/usr/bin/pdksh")
|| semanage_list_push(&list, "/bin/zsh")
|| semanage_list_push(&list, "/bin/sh")
|| semanage_list_push(&list, "/bin/bash"))
goto fail;
return list;
fail:
semanage_list_destroy(&list);
return NULL;
}
static semanage_list_t *get_shell_list(void)
{
FILE *shells;
char *temp = NULL;
semanage_list_t *list = NULL;
size_t buff_len = 0;
ssize_t len;
shells = fopen(PATH_SHELLS_FILE, "r");
if (!shells)
return default_shell_list();
while ((len = getline(&temp, &buff_len, shells)) > 0) {
if (temp[len-1] == '\n') temp[len-1] = 0;
if (strcmp(temp, PATH_NOLOGIN_SHELL)) {
if (semanage_list_push(&list, temp)) {
free(temp);
semanage_list_destroy(&list);
return default_shell_list();
}
}
}
free(temp);
return list;
}
/* Helper function called via semanage_fcontext_iterate() */
static int fcontext_matches(const semanage_fcontext_t *fcontext, void *varg)
{
const char *oexpr = semanage_fcontext_get_expr(fcontext);
fc_match_handle_t *handp = varg;
struct Ustr *expr;
regex_t re;
int type, retval = -1;
/* Only match ALL or DIR */
type = semanage_fcontext_get_type(fcontext);
if (type != SEMANAGE_FCONTEXT_ALL && type != SEMANAGE_FCONTEXT_ALL)
return 0;
/* Convert oexpr into a Ustr and anchor it at the beginning */
expr = ustr_dup_cstr("^");
if (expr == USTR_NULL)
goto done;
if (!ustr_add_cstr(&expr, oexpr))
goto done;
/* Strip off trailing ".+" or ".*" */
if (ustr_cmp_suffix_cstr_eq(expr, ".+") ||
ustr_cmp_suffix_cstr_eq(expr, ".*")) {
if (!ustr_del(&expr, 2))
goto done;
}
/* Strip off trailing "(/.*)?" */
if (ustr_cmp_suffix_cstr_eq(expr, "(/.*)?")) {
if (!ustr_del(&expr, 6))
goto done;
}
if (ustr_cmp_suffix_cstr_eq(expr, "/")) {
if (!ustr_del(&expr, 1))
goto done;
}
/* Append pattern to eat up trailing slashes */
if (!ustr_add_cstr(&expr, "/*$"))
goto done;
/* Check dir against expr */
if (regcomp(&re, ustr_cstr(expr), REG_EXTENDED) != 0)
goto done;
if (regexec(&re, handp->dir, 0, NULL, 0) == 0)
handp->matched = 1;
regfree(&re);
retval = 0;
done:
ustr_free(expr);
return retval;
}
static semanage_list_t *get_home_dirs(genhomedircon_settings_t * s)
{
semanage_list_t *homedir_list = NULL;
semanage_list_t *shells = NULL;
fc_match_handle_t hand;
char *rbuf = NULL;
char *path = NULL;
long rbuflen;
uid_t temp, minuid = 500, maxuid = 60000;
int minuid_set = 0;
struct passwd pwstorage, *pwbuf;
struct stat buf;
int retval;
path = semanage_findval(PATH_ETC_USERADD, "HOME", "=");
if (path && *path) {
if (semanage_list_push(&homedir_list, path))
goto fail;
}
free(path);
path = semanage_findval(PATH_ETC_LIBUSER, "LU_HOMEDIRECTORY", "=");
if (path && *path) {
if (semanage_list_push(&homedir_list, path))
goto fail;
}
free(path);
path = NULL;
if (!homedir_list) {
if (semanage_list_push(&homedir_list, PATH_DEFAULT_HOME)) {
goto fail;
}
}
if (!stat(PATH_EXPORT_HOME, &buf)) {
if (S_ISDIR(buf.st_mode)) {
if (semanage_list_push(&homedir_list, PATH_EXPORT_HOME)) {
goto fail;
}
}
}
if (!(s->usepasswd))
return homedir_list;
shells = get_shell_list();
assert(shells);
path = semanage_findval(PATH_ETC_LOGIN_DEFS, "UID_MIN", NULL);
if (path && *path) {
temp = atoi(path);
minuid = temp;
minuid_set = 1;
}
free(path);
path = NULL;
path = semanage_findval(PATH_ETC_LOGIN_DEFS, "UID_MAX", NULL);
if (path && *path) {
temp = atoi(path);
maxuid = temp;
}
free(path);
path = NULL;
path = semanage_findval(PATH_ETC_LIBUSER, "LU_UIDNUMBER", "=");
if (path && *path) {
temp = atoi(path);
if (!minuid_set || temp < minuid) {
minuid = temp;
minuid_set = 1;
}
}
free(path);
path = NULL;
rbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
if (rbuflen <= 0)
goto fail;
rbuf = malloc(rbuflen);
if (rbuf == NULL)
goto fail;
setpwent();
while ((retval = getpwent_r(&pwstorage, rbuf, rbuflen, &pwbuf)) == 0) {
if (pwbuf->pw_uid < minuid || pwbuf->pw_uid > maxuid)
continue;
if (!semanage_list_find(shells, pwbuf->pw_shell))
continue;
int len = strlen(pwbuf->pw_dir) -1;
for(; len > 0 && pwbuf->pw_dir[len] == '/'; len--) {
pwbuf->pw_dir[len] = '\0';
}
if (strcmp(pwbuf->pw_dir, "/") == 0)
continue;
if (ignore(pwbuf->pw_dir))
continue;
if (semanage_str_count(pwbuf->pw_dir, '/') <= 1)
continue;
if (!(path = strdup(pwbuf->pw_dir))) {
break;
}
semanage_rtrim(path, '/');
if (!semanage_list_find(homedir_list, path)) {
/*
* Now check for an existing file context that matches
* so we don't label a non-homedir as a homedir.
*/
hand.dir = path;
hand.matched = 0;
if (semanage_fcontext_iterate(s->h_semanage,
fcontext_matches, &hand) == STATUS_ERR)
goto fail;
/* NOTE: old genhomedircon printed a warning on match */
if (hand.matched) {
WARN(s->h_semanage, "%s homedir %s or its parent directory conflicts with a file context already specified in the policy. This usually indicates an incorrectly defined system account. If it is a system account please make sure its uid is less than %u or greater than %u or its login shell is /sbin/nologin.", pwbuf->pw_name, pwbuf->pw_dir, minuid, maxuid);
} else {
if (semanage_list_push(&homedir_list, path))
goto fail;
}
}
free(path);
path = NULL;
}
if (retval && retval != ENOENT) {
WARN(s->h_semanage, "Error while fetching users. "
"Returning list so far.");
}
if (semanage_list_sort(&homedir_list))
goto fail;
endpwent();
free(rbuf);
semanage_list_destroy(&shells);
return homedir_list;
fail:
endpwent();
free(rbuf);
free(path);
semanage_list_destroy(&homedir_list);
semanage_list_destroy(&shells);
return NULL;
}
/**
* @param out the FILE to put all the output in.
* @return 0 on success
*/
static int write_file_context_header(FILE * out)
{
if (fprintf(out, COMMENT_FILE_CONTEXT_HEADER) < 0) {
return STATUS_ERR;
}
return STATUS_SUCCESS;
}
/* Predicates for use with semanage_slurp_file_filter() the homedir_template
* file currently contains lines that serve as the template for a user's
* homedir.
*
* It also contains lines that are the template for the parent of a
* user's home directory.
*
* Currently, the only lines that apply to the the root of a user's home
* directory are all prefixed with the string "HOME_ROOT". All other
* lines apply to a user's home directory. If this changes the
* following predicates need to change to reflect that.
*/
static int HOME_ROOT_PRED(const char *string)
{
return semanage_is_prefix(string, TEMPLATE_HOME_ROOT);
}
static int HOME_DIR_PRED(const char *string)
{
return semanage_is_prefix(string, TEMPLATE_HOME_DIR);
}
/* new names */
static int USERNAME_CONTEXT_PRED(const char *string)
{
return (int)(
(strstr(string, TEMPLATE_USERNAME) != NULL) ||
(strstr(string, TEMPLATE_USERID) != NULL)
);
}
/* This will never match USER if USERNAME or USERID are found. */
static int USER_CONTEXT_PRED(const char *string)
{
if (USERNAME_CONTEXT_PRED(string))
return 0;
return (int)(strstr(string, TEMPLATE_USER) != NULL);
}
/* make_tempate
* @param s the settings holding the paths to various files
* @param pred function pointer to function to use as filter for slurp
* file filter
* @return a list of lines from the template file with inappropriate
* lines filtered out.
*/
static semanage_list_t *make_template(genhomedircon_settings_t * s,
int (*pred) (const char *))
{
FILE *template_file = NULL;
semanage_list_t *template_data = NULL;
template_file = fopen(s->homedir_template_path, "r");
if (!template_file)
return NULL;
template_data = semanage_slurp_file_filter(template_file, pred);
fclose(template_file);
return template_data;
}
static Ustr *replace_all(const char *str, const replacement_pair_t * repl)
{
Ustr *retval = USTR_NULL;
int i;
if (!str || !repl)
goto done;
if (!(retval = ustr_dup_cstr(str)))
goto done;
for (i = 0; repl[i].search_for; i++) {
ustr_replace_cstr(&retval, repl[i].search_for,
repl[i].replace_with, 0);
}
if (ustr_enomem(retval))
ustr_sc_free(&retval);
done:
return retval;
}
static const char * extract_context(Ustr *line)
{
const char whitespace[] = " \t\n";
size_t off, len;
/* check for trailing whitespace */
off = ustr_spn_chrs_rev(line, 0, whitespace, strlen(whitespace));
/* find the length of the last field in line */
len = ustr_cspn_chrs_rev(line, off, whitespace, strlen(whitespace));
if (len == 0)
return NULL;
return ustr_cstr(line) + ustr_len(line) - (len + off);
}
static int check_line(genhomedircon_settings_t * s, Ustr *line)
{
sepol_context_t *ctx_record = NULL;
const char *ctx_str;
int result;
ctx_str = extract_context(line);
if (!ctx_str)
return STATUS_ERR;
result = sepol_context_from_string(s->h_semanage->sepolh,
ctx_str, &ctx_record);
if (result == STATUS_SUCCESS && ctx_record != NULL) {
sepol_msg_set_callback(s->h_semanage->sepolh, NULL, NULL);
result = sepol_context_check(s->h_semanage->sepolh,
s->policydb, ctx_record);
sepol_msg_set_callback(s->h_semanage->sepolh,
semanage_msg_relay_handler, s->h_semanage);
sepol_context_free(ctx_record);
}
return result;
}
static int write_replacements(genhomedircon_settings_t * s, FILE * out,
const semanage_list_t * tpl,
const replacement_pair_t *repl)
{
Ustr *line = USTR_NULL;
for (; tpl; tpl = tpl->next) {
line = replace_all(tpl->data, repl);
if (!line)
goto fail;
if (check_line(s, line) == STATUS_SUCCESS) {
if (!ustr_io_putfileline(&line, out))
goto fail;
}
ustr_sc_free(&line);
}
return STATUS_SUCCESS;
fail:
ustr_sc_free(&line);
return STATUS_ERR;
}
static int write_home_dir_context(genhomedircon_settings_t * s, FILE * out,
semanage_list_t * tpl, const genhomedircon_user_entry_t *user)
{
replacement_pair_t repl[] = {
{.search_for = TEMPLATE_SEUSER,.replace_with = user->sename},
{.search_for = TEMPLATE_HOME_DIR,.replace_with = user->home},
{.search_for = TEMPLATE_ROLE,.replace_with = user->prefix},
{.search_for = TEMPLATE_LEVEL,.replace_with = user->level},
{NULL, NULL}
};
if (strcmp(user->name, FALLBACK_NAME) == 0) {
if (fprintf(out, COMMENT_USER_HOME_CONTEXT, FALLBACK_SENAME) < 0)
return STATUS_ERR;
} else {
if (fprintf(out, COMMENT_USER_HOME_CONTEXT, user->name) < 0)
return STATUS_ERR;
}
return write_replacements(s, out, tpl, repl);
}
static int write_home_root_context(genhomedircon_settings_t * s, FILE * out,
semanage_list_t * tpl, char *homedir)
{
replacement_pair_t repl[] = {
{.search_for = TEMPLATE_HOME_ROOT,.replace_with = homedir},
{NULL, NULL}
};
return write_replacements(s, out, tpl, repl);
}
static int write_username_context(genhomedircon_settings_t * s, FILE * out,
semanage_list_t * tpl,
const genhomedircon_user_entry_t *user)
{
replacement_pair_t repl[] = {
{.search_for = TEMPLATE_USERNAME,.replace_with = user->name},
{.search_for = TEMPLATE_USERID,.replace_with = user->uid},
{.search_for = TEMPLATE_ROLE,.replace_with = user->prefix},
{.search_for = TEMPLATE_SEUSER,.replace_with = user->sename},
{NULL, NULL}
};
return write_replacements(s, out, tpl, repl);
}
static int write_user_context(genhomedircon_settings_t * s, FILE * out,
semanage_list_t * tpl, const genhomedircon_user_entry_t *user)
{
replacement_pair_t repl[] = {
{.search_for = TEMPLATE_USER,.replace_with = user->name},
{.search_for = TEMPLATE_ROLE,.replace_with = user->prefix},
{.search_for = TEMPLATE_SEUSER,.replace_with = user->sename},
{NULL, NULL}
};
return write_replacements(s, out, tpl, repl);
}
static int user_sort_func(semanage_user_t ** arg1, semanage_user_t ** arg2)
{
return strcmp(semanage_user_get_name(*arg1),
semanage_user_get_name(*arg2));
}
static int name_user_cmp(char *key, semanage_user_t ** val)
{
return strcmp(key, semanage_user_get_name(*val));
}
static int push_user_entry(genhomedircon_user_entry_t ** list, const char *n,
const char *u, const char *g, const char *sen,
const char *pre, const char *h, const char *l)
{
genhomedircon_user_entry_t *temp = NULL;
char *name = NULL;
char *uid = NULL;
char *gid = NULL;
char *sename = NULL;
char *prefix = NULL;
char *home = NULL;
char *level = NULL;
temp = malloc(sizeof(genhomedircon_user_entry_t));
if (!temp)
goto cleanup;
name = strdup(n);
if (!name)
goto cleanup;
uid = strdup(u);
if (!uid)
goto cleanup;
gid = strdup(g);
if (!gid)
goto cleanup;
sename = strdup(sen);
if (!sename)
goto cleanup;
prefix = strdup(pre);
if (!prefix)
goto cleanup;
home = strdup(h);
if (!home)
goto cleanup;
level = strdup(l);
if (!level)
goto cleanup;
temp->name = name;
temp->uid = uid;
temp->gid = gid;
temp->sename = sename;
temp->prefix = prefix;
temp->home = home;
temp->level = level;
temp->next = (*list);
(*list) = temp;
return STATUS_SUCCESS;
cleanup:
free(name);
free(uid);
free(gid);
free(sename);
free(prefix);
free(home);
free(level);
free(temp);
return STATUS_ERR;
}
static void pop_user_entry(genhomedircon_user_entry_t ** list)
{
genhomedircon_user_entry_t *temp;
if (!list || !(*list))
return;
temp = *list;
*list = temp->next;
free(temp->name);
free(temp->uid);
free(temp->gid);
free(temp->sename);
free(temp->prefix);
free(temp->home);
free(temp->level);
free(temp);
}
static int setup_fallback_user(genhomedircon_settings_t * s)
{
semanage_seuser_t **seuser_list = NULL;
unsigned int nseusers = 0;
semanage_user_key_t *key = NULL;
semanage_user_t *u = NULL;
const char *name = NULL;
const char *seuname = NULL;
const char *prefix = NULL;
const char *level = NULL;
unsigned int i;
int retval;
int errors = 0;
retval = semanage_seuser_list(s->h_semanage, &seuser_list, &nseusers);
if (retval < 0 || (nseusers < 1)) {
/* if there are no users, this function can't do any other work */
return errors;
}
for (i = 0; i < nseusers; i++) {
name = semanage_seuser_get_name(seuser_list[i]);
if (strcmp(name, DEFAULT_LOGIN) == 0) {
seuname = semanage_seuser_get_sename(seuser_list[i]);
/* find the user structure given the name */
if (semanage_user_key_create(s->h_semanage, seuname,
&key) < 0) {
errors = STATUS_ERR;
break;
}
if (semanage_user_query(s->h_semanage, key, &u) < 0)
{
prefix = name;
level = FALLBACK_LEVEL;
}
else
{
prefix = semanage_user_get_prefix(u);
level = semanage_user_get_mlslevel(u);
if (!level)
level = FALLBACK_LEVEL;
}
if (push_user_entry(&(s->fallback), FALLBACK_NAME,
FALLBACK_UIDGID, FALLBACK_UIDGID,
seuname, prefix, "", level) != 0)
errors = STATUS_ERR;
semanage_user_key_free(key);
if (u)
semanage_user_free(u);
break;
}
}
for (i = 0; i < nseusers; i++)
semanage_seuser_free(seuser_list[i]);
free(seuser_list);
return errors;
}
static genhomedircon_user_entry_t *get_users(genhomedircon_settings_t * s,
int *errors)
{
genhomedircon_user_entry_t *head = NULL;
semanage_seuser_t **seuser_list = NULL;
unsigned int nseusers = 0;
semanage_user_t **user_list = NULL;
unsigned int nusers = 0;
semanage_user_t **u = NULL;
const char *name = NULL;
const char *seuname = NULL;
const char *prefix = NULL;
const char *level = NULL;
char uid[11];
char gid[11];
struct passwd pwstorage, *pwent = NULL;
unsigned int i;
long rbuflen;
char *rbuf = NULL;
int retval;
*errors = 0;
retval = semanage_seuser_list(s->h_semanage, &seuser_list, &nseusers);
if (retval < 0 || (nseusers < 1)) {
/* if there are no users, this function can't do any other work */
return NULL;
}
if (semanage_user_list(s->h_semanage, &user_list, &nusers) < 0) {
nusers = 0;
}
qsort(user_list, nusers, sizeof(semanage_user_t *),
(int (*)(const void *, const void *))&user_sort_func);
/* Allocate space for the getpwnam_r buffer */
rbuflen = sysconf(_SC_GETPW_R_SIZE_MAX);
if (rbuflen <= 0)
goto cleanup;
rbuf = malloc(rbuflen);
if (rbuf == NULL)
goto cleanup;
for (i = 0; i < nseusers; i++) {
seuname = semanage_seuser_get_sename(seuser_list[i]);
name = semanage_seuser_get_name(seuser_list[i]);
if (strcmp(name,"root") && strcmp(seuname, s->fallback->sename) == 0)
continue;
if (strcmp(name, DEFAULT_LOGIN) == 0)
continue;
if (strcmp(name, TEMPLATE_SEUSER) == 0)
continue;
/* %groupname syntax */
if (name[0] == '%')
continue;
/* find the user structure given the name */
u = bsearch(seuname, user_list, nusers, sizeof(semanage_user_t *),
(int (*)(const void *, const void *))
&name_user_cmp);
if (u) {
prefix = semanage_user_get_prefix(*u);
level = semanage_user_get_mlslevel(*u);
if (!level)
level = FALLBACK_LEVEL;
} else {
prefix = name;
level = FALLBACK_LEVEL;
}
retval = getpwnam_r(name, &pwstorage, rbuf, rbuflen, &pwent);
if (retval != 0 || pwent == NULL) {
if (retval != 0 && retval != ENOENT) {
*errors = STATUS_ERR;
goto cleanup;
}
WARN(s->h_semanage,
"user %s not in password file", name);
continue;
}
int len = strlen(pwent->pw_dir) -1;
for(; len > 0 && pwent->pw_dir[len] == '/'; len--) {
pwent->pw_dir[len] = '\0';
}
if (strcmp(pwent->pw_dir, "/") == 0) {
/* don't relabel / genhomdircon checked to see if root
* was the user and if so, set his home directory to
* /root */
continue;
}
if (ignore(pwent->pw_dir))
continue;
len = snprintf(uid, sizeof(uid), "%u", pwent->pw_uid);
if (len < 0 || len >= (int)sizeof(uid)) {
*errors = STATUS_ERR;
goto cleanup;
}
len = snprintf(gid, sizeof(gid), "%u", pwent->pw_gid);
if (len < 0 || len >= (int)sizeof(gid)) {
*errors = STATUS_ERR;
goto cleanup;
}
if (push_user_entry(&head, name, uid, gid, seuname,
prefix, pwent->pw_dir, level) != STATUS_SUCCESS) {
*errors = STATUS_ERR;
break;
}
}
cleanup:
free(rbuf);
if (*errors) {
for (; head; pop_user_entry(&head)) {
/* the pop function takes care of all the cleanup
so the loop body is just empty */
}
}
for (i = 0; i < nseusers; i++) {
semanage_seuser_free(seuser_list[i]);
}
free(seuser_list);
for (i = 0; i < nusers; i++) {
semanage_user_free(user_list[i]);
}
free(user_list);
return head;
}
static int write_gen_home_dir_context(genhomedircon_settings_t * s, FILE * out,
semanage_list_t * username_context_tpl,
semanage_list_t * user_context_tpl,
semanage_list_t * homedir_context_tpl)
{
genhomedircon_user_entry_t *users;
int errors = 0;
users = get_users(s, &errors);
if (!users && errors) {
return STATUS_ERR;
}
for (; users; pop_user_entry(&users)) {
if (write_home_dir_context(s, out, homedir_context_tpl, users))
goto err;
if (write_username_context(s, out, username_context_tpl, users))
goto err;
if (write_user_context(s, out, user_context_tpl, users))
goto err;
}
return STATUS_SUCCESS;
err:
for (; users; pop_user_entry(&users)) {
/* the pop function takes care of all the cleanup
* so the loop body is just empty */
}
return STATUS_ERR;
}
/**
* @param s settings structure, stores various paths etc. Must never be NULL
* @param out the FILE to put all the output in.
* @return 0 on success
*/
static int write_context_file(genhomedircon_settings_t * s, FILE * out)
{
semanage_list_t *homedirs = NULL;
semanage_list_t *h = NULL;
semanage_list_t *homedir_context_tpl = NULL;
semanage_list_t *homeroot_context_tpl = NULL;
semanage_list_t *username_context_tpl = NULL;
semanage_list_t *user_context_tpl = NULL;
int retval = STATUS_SUCCESS;
homedir_context_tpl = make_template(s, &HOME_DIR_PRED);
homeroot_context_tpl = make_template(s, &HOME_ROOT_PRED);
username_context_tpl = make_template(s, &USERNAME_CONTEXT_PRED);
user_context_tpl = make_template(s, &USER_CONTEXT_PRED);
if (!homedir_context_tpl
&& !homeroot_context_tpl
&& !username_context_tpl
&& !user_context_tpl)
goto done;
if (write_file_context_header(out) != STATUS_SUCCESS) {
retval = STATUS_ERR;
goto done;
}
if (setup_fallback_user(s) != 0) {
retval = STATUS_ERR;
goto done;
}
if (homedir_context_tpl || homeroot_context_tpl) {
homedirs = get_home_dirs(s);
if (!homedirs) {
WARN(s->h_semanage,
"no home directories were available, exiting without writing");
goto done;
}
for (h = homedirs; h; h = h->next) {
Ustr *temp = ustr_dup_cstr(h->data);
if (!temp || !ustr_add_cstr(&temp, "/" FALLBACK_NAME)) {
ustr_sc_free(&temp);
retval = STATUS_ERR;
goto done;
}
free(s->fallback->home);
s->fallback->home = (char*) ustr_cstr(temp);
if (write_home_dir_context(s, out, homedir_context_tpl,
s->fallback) != STATUS_SUCCESS) {
ustr_sc_free(&temp);
s->fallback->home = NULL;
retval = STATUS_ERR;
goto done;
}
if (write_home_root_context(s, out,
homeroot_context_tpl,
h->data) != STATUS_SUCCESS) {
ustr_sc_free(&temp);
s->fallback->home = NULL;
retval = STATUS_ERR;
goto done;
}
ustr_sc_free(&temp);
s->fallback->home = NULL;
}
}
if (user_context_tpl || username_context_tpl) {
if (write_username_context(s, out, username_context_tpl,
s->fallback) != STATUS_SUCCESS) {
retval = STATUS_ERR;
goto done;
}
if (write_user_context(s, out, user_context_tpl,
s->fallback) != STATUS_SUCCESS) {
retval = STATUS_ERR;
goto done;
}
if (write_gen_home_dir_context(s, out, username_context_tpl,
user_context_tpl, homedir_context_tpl)
!= STATUS_SUCCESS) {
retval = STATUS_ERR;
}
}
done:
/* Cleanup */
semanage_list_destroy(&homedirs);
semanage_list_destroy(&username_context_tpl);
semanage_list_destroy(&user_context_tpl);
semanage_list_destroy(&homedir_context_tpl);
semanage_list_destroy(&homeroot_context_tpl);
return retval;
}
int semanage_genhomedircon(semanage_handle_t * sh,
sepol_policydb_t * policydb,
int usepasswd,
char *ignoredirs)
{
genhomedircon_settings_t s;
FILE *out = NULL;
int retval = 0;
assert(sh);
s.homedir_template_path =
semanage_path(SEMANAGE_TMP, SEMANAGE_HOMEDIR_TMPL);
s.fcfilepath = semanage_final_path(SEMANAGE_FINAL_TMP,
SEMANAGE_FC_HOMEDIRS);
s.fallback = calloc(1, sizeof(genhomedircon_user_entry_t));
if (s.fallback == NULL) {
retval = STATUS_ERR;
goto done;
}
s.fallback->name = strdup(FALLBACK_NAME);
s.fallback->sename = strdup(FALLBACK_SENAME);
s.fallback->prefix = strdup(FALLBACK_PREFIX);
s.fallback->level = strdup(FALLBACK_LEVEL);
if (s.fallback->name == NULL
|| s.fallback->sename == NULL
|| s.fallback->prefix == NULL
|| s.fallback->level == NULL) {
retval = STATUS_ERR;
goto done;
}
if (ignoredirs) ignore_setup(ignoredirs);
s.usepasswd = usepasswd;
s.h_semanage = sh;
s.policydb = policydb;
if (!(out = fopen(s.fcfilepath, "w"))) {
/* couldn't open output file */
ERR(sh, "Could not open the file_context file for writing");
retval = STATUS_ERR;
goto done;
}
retval = write_context_file(&s, out);
done:
if (out != NULL)
fclose(out);
pop_user_entry(&(s.fallback));
ignore_free();
return retval;
}