summaryrefslogtreecommitdiffstats
path: root/src/tools/tools_util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/tools_util.c')
-rw-r--r--src/tools/tools_util.c520
1 files changed, 520 insertions, 0 deletions
diff --git a/src/tools/tools_util.c b/src/tools/tools_util.c
new file mode 100644
index 000000000..97945238e
--- /dev/null
+++ b/src/tools/tools_util.c
@@ -0,0 +1,520 @@
+/*
+ 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: [%d] [%s]\n", ret, strerror(ret)));
+ 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;
+ }
+
+ ret = EOK;
+ 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;
+}
+