/* SSSD tools_utils.c Copyright (C) Jakub Hrozek <jhrozek@redhat.com> 2009 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <talloc.h> #include <tevent.h> #include <popt.h> #include <errno.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include "config.h" #ifdef HAVE_SELINUX #include <selinux/selinux.h> #endif #include "util/util.h" #include "confdb/confdb.h" #include "db/sysdb.h" #include "tools/tools_util.h" #include "tools/sss_sync_ops.h" static int setup_db(struct tools_ctx *ctx) { char *confdb_path; int ret; /* Create the event context */ ctx->ev = tevent_context_init(ctx); if (ctx->ev == NULL) { DEBUG(1, ("Could not create event context\n")); return EIO; } confdb_path = talloc_asprintf(ctx, "%s/%s", DB_PATH, CONFDB_FILE); if (confdb_path == NULL) { return ENOMEM; } /* Connect to the conf db */ ret = confdb_init(ctx, &ctx->confdb, confdb_path); if (ret != EOK) { DEBUG(1, ("Could not initialize connection to the confdb\n")); return ret; } ret = confdb_get_domain(ctx->confdb, "local", &ctx->local); if (ret != EOK) { DEBUG(1, ("Could not get 'local' domain\n")); return ret; } /* open 'local' sysdb at default path */ ret = sysdb_domain_init(ctx, ctx->ev, ctx->local, DB_PATH, &ctx->sysdb); if (ret != EOK) { DEBUG(1, ("Could not initialize connection to the sysdb\n")); return ret; } talloc_free(confdb_path); return EOK; } /* * Print poptUsage as well as our error message */ void usage(poptContext pc, const char *error) { poptPrintUsage(pc, stderr, 0); if (error) fprintf(stderr, "%s", error); } int parse_groups(TALLOC_CTX *mem_ctx, const char *optstr, char ***_out) { char **out; char *orig, *n, *o; char delim = ','; unsigned int tokens = 1; int i; orig = talloc_strdup(mem_ctx, optstr); if (!orig) return ENOMEM; n = orig; tokens = 1; while ((n = strchr(n, delim))) { n++; tokens++; } out = talloc_array(mem_ctx, char *, tokens+1); if (!out) { talloc_free(orig); return ENOMEM; } n = o = orig; for (i = 0; i < tokens; i++) { o = n; n = strchr(n, delim); if (!n) { break; } *n = '\0'; n++; out[i] = talloc_strdup(out, o); } out[tokens-1] = talloc_strdup(out, o); out[tokens] = NULL; talloc_free(orig); *_out = out; return EOK; } int parse_group_name_domain(struct tools_ctx *tctx, char **groups) { int i; int ret; char *name = NULL; char *domain = NULL; if (!groups) { return EOK; } for (i = 0; groups[i]; ++i) { ret = sss_parse_name(tctx, tctx->snctx, groups[i], &domain, &name); /* If FQDN is specified, it must be within the same domain as user */ if (domain) { if (strcmp(domain, tctx->octx->domain->name) != 0) { return EINVAL; } /* Use only groupname */ talloc_zfree(groups[i]); groups[i] = talloc_strdup(tctx, name); if (groups[i] == NULL) { return ENOMEM; } } talloc_zfree(name); talloc_zfree(domain); } talloc_zfree(name); talloc_zfree(domain); return EOK; } int parse_name_domain(struct tools_ctx *tctx, const char *fullname) { int ret; char *domain = NULL; ret = sss_parse_name(tctx, tctx->snctx, fullname, &domain, &tctx->octx->name); if (ret != EOK) { DEBUG(0, ("Cannot parse full name\n")); return ret; } DEBUG(5, ("Parsed username: %s\n", tctx->octx->name)); if (domain) { DEBUG(5, ("Parsed domain: %s\n", domain)); /* only the local domain, whatever named is allowed in tools */ if (strcasecmp(domain, tctx->local->name) != 0) { DEBUG(1, ("Invalid domain %s specified in FQDN\n", domain)); return EINVAL; } } return EOK; } int check_group_names(struct tools_ctx *tctx, char **grouplist, char **badgroup) { int ret; int i; struct ops_ctx *groupinfo; groupinfo = talloc_zero(tctx, struct ops_ctx); if (!groupinfo) { return ENOMEM; } for (i=0; grouplist[i]; ++i) { ret = sysdb_getgrnam_sync(tctx, tctx->ev, tctx->sysdb, grouplist[i], tctx->local, &groupinfo); if (ret) { DEBUG(6, ("Cannot find group %s, ret: %d\n", grouplist[i], ret)); break; } } talloc_zfree(groupinfo); *badgroup = grouplist[i]; return ret; } int id_in_range(uint32_t id, struct sss_domain_info *dom) { if (id && ((id < dom->id_min) || (dom->id_max && id > dom->id_max))) { return ERANGE; } return EOK; } int set_locale(void) { char *c; c = setlocale(LC_ALL, ""); if (c == NULL) { return EIO; } errno = 0; c = bindtextdomain(PACKAGE, LOCALEDIR); if (c == NULL) { return errno; } errno = 0; c = textdomain(PACKAGE); if (c == NULL) { return errno; } return EOK; } int init_sss_tools(struct tools_ctx **_tctx) { int ret; struct tools_ctx *tctx; tctx = talloc_zero(NULL, struct tools_ctx); if (tctx == NULL) { DEBUG(1, ("Could not allocate memory for tools context\n")); return ENOMEM; } /* Connect to the database */ ret = setup_db(tctx); if (ret != EOK) { DEBUG(1, ("Could not set up database\n")); goto fini; } ret = sss_names_init(tctx, tctx->confdb, &tctx->snctx); if (ret != EOK) { DEBUG(1, ("Could not set up parsing\n")); goto fini; } tctx->octx = talloc_zero(tctx, struct ops_ctx); if (!tctx->octx) { DEBUG(1, ("Could not allocate memory for data context\n")); ERROR("Out of memory\n"); ret = ENOMEM; goto fini; } tctx->octx->domain = tctx->local; *_tctx = tctx; ret = EOK; fini: if (ret != EOK) talloc_free(tctx); return ret; } /* * Check is path is owned by uid * returns 0 - owns * -1 - does not own * >0 - an error occured, error code */ static int is_owner(uid_t uid, const char *path) { struct stat statres; int ret; ret = stat(path, &statres); if (ret != 0) { ret = errno; DEBUG(1, ("Cannot stat %s: [%d][%s]\n", path, ret, strerror(ret))); return ret; } if (statres.st_uid == uid) { return EOK; } return -1; } static int remove_mail_spool(TALLOC_CTX *mem_ctx, const char *maildir, const char *username, uid_t uid, bool force) { int ret; char *spool_file; spool_file = talloc_asprintf(mem_ctx, "%s/%s", maildir, username); if (spool_file == NULL) { ret = ENOMEM; goto fail; } if (force == false) { /* Check the owner of the mail spool */ ret = is_owner(uid, spool_file); switch (ret) { case 0: break; case -1: DEBUG(3, ("%s not owned by %d, not removing\n", spool_file, uid)); ret = EACCES; /* FALLTHROUGH */ default: goto fail; } } ret = unlink(spool_file); if (ret != 0) { ret = errno; DEBUG(1, ("Cannot remove() the spool file %s: [%d][%s]\n", spool_file, ret, strerror(ret))); goto fail; } fail: talloc_free(spool_file); return ret; } int remove_homedir(TALLOC_CTX *mem_ctx, const char *homedir, const char *maildir, const char *username, uid_t uid, bool force) { int ret; ret = remove_mail_spool(mem_ctx, maildir, username, uid, force); if (ret != EOK) { DEBUG(1, ("Cannot remove user's mail spool\n")); /* Should this be fatal? I don't think so. Maybe convert to ERROR? */ } if (force == false && is_owner(uid, homedir) == -1) { DEBUG(1, ("Not removing home dir - not owned by user\n")); return EPERM; } /* Remove the tree */ ret = remove_tree(homedir); if (ret != EOK) { DEBUG(1, ("Cannot remove homedir %s: %d\n", homedir, ret)); return ret; } return EOK; } /* The reason for not putting this into create_homedir * is better granularity when it comes to reporting error * messages and tracebacks in pysss */ int create_mail_spool(TALLOC_CTX *mem_ctx, const char *username, const char *maildir, uid_t uid, gid_t gid) { char *spool_file = NULL; int fd; int ret; spool_file = talloc_asprintf(mem_ctx, "%s/%s", maildir, username); if (spool_file == NULL) { ret = ENOMEM; goto fail; } selinux_file_context(spool_file); fd = open(spool_file, O_CREAT | O_WRONLY | O_EXCL, 0); if (fd < 0) { ret = errno; DEBUG(1, ("Cannot open() the spool file: [%d][%s]\n", ret, strerror(ret))); goto fail; } ret = fchmod(fd, 0600); if (ret != 0) { ret = errno; DEBUG(1, ("Cannot fchmod() the spool file: [%d][%s]\n", ret, strerror(ret))); goto fail; } ret = fchown(fd, uid, gid); if (ret != 0) { ret = errno; DEBUG(1, ("Cannot fchown() the spool file: [%d][%s]\n", ret, strerror(ret))); goto fail; } ret = fsync(fd); if (ret != 0) { ret = errno; DEBUG(1, ("Cannot fsync() the spool file: [%d][%s]\n", ret, strerror(ret))); goto fail; } ret = close(fd); if (ret != 0) { ret = errno; DEBUG(1, ("Cannot close() the spool file: [%d][%s]\n", ret, strerror(ret))); goto fail; } fail: reset_selinux_file_context(); talloc_free(spool_file); return ret; } int create_homedir(TALLOC_CTX *mem_ctx, const char *skeldir, const char *homedir, const char *username, uid_t uid, gid_t gid, mode_t default_umask) { int ret; selinux_file_context(homedir); ret = mkdir(homedir, 0); if (ret != 0) { ret = errno; DEBUG(1, ("Cannot create user's home directory: [%d][%s].\n", ret, strerror(ret))); goto done; } ret = chown(homedir, uid, gid); if (ret != 0) { ret = errno; DEBUG(1, ("Cannot chown user's home directory: [%d][%s].\n", ret, strerror(ret))); goto done; } ret = chmod(homedir, 0777 & ~default_umask); if (ret != 0) { ret = errno; DEBUG(1, ("Cannot chmod user's home directory: [%d][%s].\n", ret, strerror(ret))); goto done; } reset_selinux_file_context(); ret = copy_tree(skeldir, homedir, uid, gid); if (ret != EOK) { DEBUG(1, ("Cannot populate user's home directory: [%d][%s].\n", ret, strerror(ret))); goto done; } done: reset_selinux_file_context(); return ret; }