Beispiel #1
0
void parse_args(int argc, char* argv[])
{
    int ch;
    while((ch = getopt(argc, argv, "g:m:u:U")) != EOF) {
        switch(ch) {
        case 'm':
            opt_umask = strtoul(optarg, &optarg, 8);
            if(*optarg)
                usage("MASK is not a positive octal integer");
            break;
        case 'g':
            opt_gid = parse_gid(optarg);
            break;
        case 'u':
            opt_uid = parse_uid(optarg);
            break;
        case 'U':
            opt_gid = parse_gid(getenv("GID"));
            opt_uid = parse_uid(getenv("UID"));
            break;
        default:
            usage(0);
        }
    }
    num_sockets = argc - optind;
    if(num_sockets <= 0)
        usage("You must specify at least one socket");
    socket_names = argv + optind;
    sockets = calloc(num_sockets, sizeof(int));
}
Beispiel #2
0
static void test_parse_uid(void) {
        int r;
        uid_t uid;

        log_info("/* %s */", __func__);

        r = parse_uid("100", &uid);
        assert_se(r == 0);
        assert_se(uid == 100);

        r = parse_uid("65535", &uid);
        assert_se(r == -ENXIO);

        r = parse_uid("asdsdas", &uid);
        assert_se(r == -EINVAL);
}
Beispiel #3
0
int audit_loginuid_from_pid(pid_t pid, uid_t *uid) {
        _cleanup_free_ char *s = NULL;
        const char *p;
        uid_t u;
        int r;

        assert(uid);

        /* Audit doesn't support containers right now */
        if (detect_container(NULL) > 0)
                return -ENOTSUP;

        p = procfs_file_alloca(pid, "loginuid");

        r = read_one_line_file(p, &s);
        if (r < 0)
                return r;

        r = parse_uid(s, &u);
        if (r < 0)
                return r;

        if (u == (uid_t) -1)
                return -ENXIO;

        *uid = (uid_t) u;
        return 0;
}
Beispiel #4
0
static int condition_test_user(Condition *c) {
        uid_t id;
        int r;
        _cleanup_free_ char *username = NULL;
        const char *u;

        assert(c);
        assert(c->parameter);
        assert(c->type == CONDITION_USER);

        r = parse_uid(c->parameter, &id);
        if (r >= 0)
                return id == getuid() || id == geteuid();

        if (streq("@system", c->parameter))
                return uid_is_system(getuid()) || uid_is_system(geteuid());

        username = getusername_malloc();
        if (!username)
                return -ENOMEM;

        if (streq(username, c->parameter))
                return 1;

        if (getpid_cached() == 1)
                return streq(c->parameter, "root");

        u = c->parameter;
        r = get_user_creds(&u, &id, NULL, NULL, NULL);
        if (r < 0)
                return 0;

        return id == getuid() || id == geteuid();
}
int main(int argc, char *argv[]) {
        uid_t shift, range;
        int r;

        log_set_max_level(LOG_DEBUG);
        log_parse_environment();
        log_open();

        if (argc != 4) {
                log_error("Expected PATH SHIFT RANGE parameters.");
                return EXIT_FAILURE;
        }

        r = parse_uid(argv[2], &shift);
        if (r < 0) {
                log_error_errno(r, "Failed to parse UID shift %s.", argv[2]);
                return EXIT_FAILURE;
        }

        r = parse_gid(argv[3], &range);
        if (r < 0) {
                log_error_errno(r, "Failed to parse UID range %s.", argv[3]);
                return EXIT_FAILURE;
        }

        r = path_patch_uid(argv[1], shift, range);
        if (r < 0) {
                log_error_errno(r, "Failed to patch directory tree: %m");
                return EXIT_FAILURE;
        }

        log_info("Changed: %s", yes_no(r));

        return EXIT_SUCCESS;
}
Beispiel #6
0
int main(int argc, char *argv[]) {
        uid_t uid;

        assert_se(argc == 2);
        assert_se(parse_uid(argv[1], &uid) >= 0);

        return clean_ipc(uid) < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
Beispiel #7
0
static void test_parse_uid(void) {
        int r;
        uid_t uid;

        r = parse_uid("100", &uid);
        assert_se(r == 0);
        assert_se(uid == 100);
}
static int
service_parse_privileges(struct mail_storage_service_ctx *ctx,
			 struct mail_storage_service_user *user,
			 struct mail_storage_service_privileges *priv_r,
			 const char **error_r)
{
	const struct mail_user_settings *set = user->user_set;
	uid_t uid = (uid_t)-1;
	gid_t gid = (gid_t)-1;

	memset(priv_r, 0, sizeof(*priv_r));
	if (*set->mail_uid != '\0') {
		if (!parse_uid(set->mail_uid, &uid, error_r)) {
			*error_r = t_strdup_printf("%s (from %s)", *error_r,
						   user->uid_source);
			return -1;
		}
		if (uid < (uid_t)set->first_valid_uid ||
		    (set->last_valid_uid != 0 &&
		     uid > (uid_t)set->last_valid_uid)) {
			*error_r = t_strdup_printf(
				"Mail access for users with UID %s not permitted "
				"(see first_valid_uid in config file, uid from %s).",
				dec2str(uid), user->uid_source);
			return -1;
		}
	}
	priv_r->uid = uid;
	priv_r->uid_source = user->uid_source;

	if (*set->mail_gid != '\0') {
		if (!parse_gid(set->mail_gid, &gid, error_r)) {
			*error_r = t_strdup_printf("%s (from %s)", *error_r,
						   user->gid_source);
			return -1;
		}
		if (gid < (gid_t)set->first_valid_gid ||
		    (set->last_valid_gid != 0 &&
		     gid > (gid_t)set->last_valid_gid)) {
			*error_r = t_strdup_printf(
				"Mail access for users with GID %s not permitted "
				"(see first_valid_gid in config file, gid from %s).",
				dec2str(gid), user->gid_source);
			return -1;
		}
	}
	priv_r->gid = gid;
	priv_r->gid_source = user->gid_source;

	/* variable strings are expanded in mail_user_init(),
	   but we need the home and chroot sooner so do them separately here. */
	priv_r->home = user_expand_varstr(ctx, user, priv_r,
					  user->user_set->mail_home);
	priv_r->chroot = user_expand_varstr(ctx, user, priv_r,
					    user->user_set->mail_chroot);
	return 0;
}
Beispiel #9
0
void parse_args(int argc, char* argv[])
{
  int ch;
  while((ch = getopt(argc, argv, "g:u:U")) != EOF) {
    switch(ch) {
    case 'g': opt_gid = parse_gid(optarg); break;
    case 'u': opt_uid = parse_uid(optarg); break;
    case 'U':
      opt_gid = parse_gid(getenv("GID"));
      opt_uid = parse_uid(getenv("UID"));
      break;
    default:
      usage(0);
    }
  }
  if((argc - optind) % 2)
    usage("Missing port number");
  num_sockets = (argc - optind) / 2;
  if(num_sockets <= 0)
    usage("You must specify at least one socket");
  socket_names = argv + optind;
  sockets = calloc(num_sockets, sizeof(int));
}
Beispiel #10
0
int uid_range_add_str(UidRange **p, unsigned *n, const char *s) {
        uid_t start, nr;
        const char *t;
        int r;

        assert(p);
        assert(n);
        assert(s);

        t = strchr(s, '-');
        if (t) {
                char *b;
                uid_t end;

                b = strndupa(s, t - s);
                r = parse_uid(b, &start);
                if (r < 0)
                        return r;

                r = parse_uid(t+1, &end);
                if (r < 0)
                        return r;

                if (end < start)
                        return -EINVAL;

                nr = end - start + 1;
        } else {
                r = parse_uid(s, &start);
                if (r < 0)
                        return r;

                nr = 1;
        }

        return uid_range_add(p, n, start, nr);
}
Beispiel #11
0
int audit_loginuid_from_pid(pid_t pid, uid_t *uid) {
        char *s;
        uid_t u;
        int r;

        assert(uid);

        /* Only use audit login uid if we are executed with sufficient
         * capabilities so that pam_loginuid could do its job. If we
         * are lacking the CAP_AUDIT_CONTROL capabality we most likely
         * are being run in a container and /proc/self/loginuid is
         * useless since it probably contains a uid of the host
         * system. */

        if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0)
                return -ENOENT;

        /* Audit doesn't support containers right now */
        if (detect_container(NULL) > 0)
                return -ENOTSUP;

        if (pid == 0)
                r = read_one_line_file("/proc/self/loginuid", &s);
        else {
                char *p;

                if (asprintf(&p, "/proc/%lu/loginuid", (unsigned long) pid) < 0)
                        return -ENOMEM;

                r = read_one_line_file(p, &s);
                free(p);
        }

        if (r < 0)
                return r;

        r = parse_uid(s, &u);
        free(s);

        if (r < 0)
                return r;

        if (u == (uid_t) -1)
                return -ENOENT;

        *uid = (uid_t) u;
        return 0;
}
Beispiel #12
0
_public_ int sd_session_get_uid(const char *session, uid_t *uid) {
        int r;
        _cleanup_free_ char *p = NULL, *s = NULL;

        assert_return(uid, -EINVAL);

        r = file_of_session(session, &p);
        if (r < 0)
                return r;

        r = parse_env_file(p, NEWLINE, "UID", &s, NULL);
        if (r < 0)
                return r;

        if (!s)
                return -EIO;

        return parse_uid(s, uid);
}
Beispiel #13
0
static int uid_from_file_name(const char *filename, uid_t *uid) {
        const char *p, *e, *u;

        p = startswith(filename, "core.");
        if (!p)
                return -EINVAL;

        /* Skip the comm field */
        p = strchr(p, '.');
        if (!p)
                return -EINVAL;
        p++;

        /* Find end up UID */
        e = strchr(p, '.');
        if (!e)
                return -EINVAL;

        u = strndupa(p, e-p);
        return parse_uid(u, uid);
}
Beispiel #14
0
int audit_loginuid_from_pid(pid_t pid, uid_t *uid) {
        _cleanup_free_ char *s = NULL;
        const char *p;
        uid_t u;
        int r;

        assert(uid);

        p = procfs_file_alloca(pid, "loginuid");

        r = read_one_line_file(p, &s);
        if (r < 0)
                return r;

        r = parse_uid(s, &u);
        if (r < 0)
                return r;

        *uid = (uid_t) u;
        return 0;
}
Beispiel #15
0
/*
 * parse_netid_str()
 *
 * Parse uid and group information from the passed string.
 *
 * The format of the string passed is
 * 	uid:gid,grp,grp, ...
 *
 */
static int
parse_netid_str(char *s, struct netid_userdata *argp)
{
	char	*p;
	int	err;

	/* get uid */
	err = parse_uid(s, argp);
	if (err != __NSW_SUCCESS)
		return (err);

	/* Now get the group list */
	p = strchr(s, ':');
	if (!p) {
		syslog(LOG_ERR,
		    "netname2user: missing group id list in '%s'", s);
		return (__NSW_NOTFOUND);
	}
	++p;			/* skip ':' */
	err = parse_gidlist(p, argp);
	return (err);
}
Beispiel #16
0
/* auth_flatfile_init:
 * Initialise the driver. Reads the config directives. */
int auth_flatfile_init(void) {
    char *s;
    int ret = 0;

    /* Obtain uid to use */
    if ((s = config_get_string("auth-flatfile-mail-user"))) {
        if (!parse_uid(s, &virtual_uid)) {
            log_print(LOG_ERR, _("auth_flatfile_init: auth-flatfile-mail-user directive `%s' does not make sense"), s);
            goto fail;
        }
    } else {
        log_print(LOG_ERR, _("auth_flatfile_init: no auth-flatfile-mail-user directive in config"));
        goto fail;
    }

    /* Obtain gid to use */
    if ((s = config_get_string("auth-flatfile-mail-group"))) {
        if (!parse_gid(s, &virtual_gid)) {
            log_print(LOG_ERR, _("auth_flatfile_init: auth-flatfile-mail-group directive `%s' does not make sense"), s);
            goto fail;
        }
    } else {
        log_print(LOG_ERR, _("auth_flatfile_init: no auth-flatfile-mail-group directive in config"));
        goto fail;
    }

    /* Obtain path template to passwd file */
    if ((s = config_get_string("auth-flatfile-passwd-file"))) {
	user_passwd_file_template = s;
    } else {
        log_print(LOG_ERR, _("auth_flatfile_init: no auth-flatfile-passwd-file directive in config"));
        goto fail;
    }

    ret = 1;

fail:
    return ret;
}
Beispiel #17
0
int main (int argc, char **argv) {
	char *pidfile=NULL, *jail=NULL, *exec=NULL;
	int uid=-1,gid=-1;
	unsigned int i;
	char **newargv;

	openlog(PROGRAMNAME, LOG_PID, LOG_DAEMON);

	{
		int c=0;
		char *tuser=NULL, *tgroup=NULL, *texec=NULL;
		while (c != -1) {
			int option_index = 0;
			static struct option long_options[] = {
				{"pidfile", required_argument, NULL, 'p'},
				{"jail", required_argument, NULL, 'j'},
				{"exec", required_argument, NULL, 'x'},
				{"user", required_argument, NULL, 'u'},
				{"group", required_argument, NULL, 'g'},
				{"help", no_argument, NULL, 'h'},
				{"version", no_argument, NULL, 'V'},
				{NULL, 0, NULL, 0}
			};
		 	c = getopt_long(argc, argv, "j:p:u:g:x:hv",long_options, &option_index);
			switch (c) {
			case 'j':
				jail = ending_slash(optarg);
				break;
			case 'p':
				pidfile = strdup(optarg);
				break;
			case 'u':
				tuser = strdup(optarg);
				break;
			case 'g':
				tgroup = strdup(optarg);
				break;
			case 'x':
				texec = strdup(optarg);
				break;
			case 'h':
			case 'V':
				print_usage();
				exit(1);
			}
		}
		uid = parse_uid(tuser);
		gid = parse_gid(tgroup);
		exec = test_jail_and_exec(jail,texec);
		/* construct the new argv from all leftover options */
		newargv = malloc0((2 + argc - optind)*sizeof(char *));
		newargv[0] = exec;
		c = 1;
		while (optind < argc) {
			newargv[c] = strdup(argv[optind]);
			c++;
			optind++;
		}
		free(tuser);
		free(tgroup);
		free(texec);
	}
	
	if (pidfile) {
		FILE *pidfilefd = fopen(pidfile, "w");
		int pid = getpid();
		if (pidfilefd && fprintf(pidfilefd, "%d",pid)>=0) {
			fclose(pidfilefd);
		} else {
			syslog(LOG_NOTICE, "failed to write PID into %s", pidfile);
		}
	}
	
	/* open file descriptors can be used to break out of a chroot, so we close all of them, except for stdin,stdout and stderr */
#ifdef OPEN_MAX
    i = OPEN_MAX;
#elif defined(NOFILE)
    i = NOFILE;
#else
    i = getdtablesize();
#endif
	while (--i > 2) {
		while (close(i) != 0 && errno == EINTR);
	}
	
	if (chdir(jail)) {
		syslog(LOG_ERR, "abort, could not change directory chdir() to the jail %s: %s", jail,strerror(errno));
		exit(33);
	}
	if (chroot(jail)) {
		syslog(LOG_ERR, "abort, could not change root chroot() to the jail %s: %s", jail,strerror(errno));
		exit(35);
	}
	if (gid != -1 && setgid(gid)<0) {
		syslog(LOG_ERR, "abort, could not setgid %d: %s", gid,strerror(errno));
		exit(37);
	}
	if (uid != -1 && setuid(uid)<0) {
		syslog(LOG_ERR, "abort, could not setuid %d: %s", uid,strerror(errno));
		exit(39);
	}
	syslog(LOG_NOTICE,"executing %s in jail %s",exec,jail);
	execv(exec, newargv);
	syslog(LOG_ERR, "error: failed to execute %s in jail %s: %s",exec,jail,strerror(errno));
	exit(31);
}
Beispiel #18
0
int take_etc_passwd_lock(const char *root) {

        struct flock flock = {
                .l_type = F_WRLCK,
                .l_whence = SEEK_SET,
                .l_start = 0,
                .l_len = 0,
        };

        const char *path;
        int fd, r;

        /* This is roughly the same as lckpwdf(), but not as awful. We
         * don't want to use alarm() and signals, hence we implement
         * our own trivial version of this.
         *
         * Note that shadow-utils also takes per-database locks in
         * addition to lckpwdf(). However, we don't given that they
         * are redundant as they invoke lckpwdf() first and keep
         * it during everything they do. The per-database locks are
         * awfully racy, and thus we just won't do them. */

        if (root)
                path = prefix_roota(root, "/etc/.pwd.lock");
        else
                path = "/etc/.pwd.lock";

        fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
        if (fd < 0)
                return -errno;

        r = fcntl(fd, F_SETLKW, &flock);
        if (r < 0) {
                safe_close(fd);
                return -errno;
        }

        return fd;
}

bool valid_user_group_name(const char *u) {
        const char *i;
        long sz;

        /* Checks if the specified name is a valid user/group name. */

        if (isempty(u))
                return false;

        if (!(u[0] >= 'a' && u[0] <= 'z') &&
            !(u[0] >= 'A' && u[0] <= 'Z') &&
            u[0] != '_')
                return false;

        for (i = u+1; *i; i++) {
                if (!(*i >= 'a' && *i <= 'z') &&
                    !(*i >= 'A' && *i <= 'Z') &&
                    !(*i >= '0' && *i <= '9') &&
                    *i != '_' &&
                    *i != '-')
                        return false;
        }

        sz = sysconf(_SC_LOGIN_NAME_MAX);
        assert_se(sz > 0);

        if ((size_t) (i-u) > (size_t) sz)
                return false;

        if ((size_t) (i-u) > UT_NAMESIZE - 1)
                return false;

        return true;
}

bool valid_user_group_name_or_id(const char *u) {

        /* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the right
         * range, and not the invalid user ids. */

        if (isempty(u))
                return false;

        if (valid_user_group_name(u))
                return true;

        return parse_uid(u, NULL) >= 0;
}

bool valid_gecos(const char *d) {

        if (!d)
                return false;

        if (!utf8_is_valid(d))
                return false;

        if (string_has_cc(d, NULL))
                return false;

        /* Colons are used as field separators, and hence not OK */
        if (strchr(d, ':'))
                return false;

        return true;
}

bool valid_home(const char *p) {

        if (isempty(p))
                return false;

        if (!utf8_is_valid(p))
                return false;

        if (string_has_cc(p, NULL))
                return false;

        if (!path_is_absolute(p))
                return false;

        if (!path_is_safe(p))
                return false;

        /* Colons are used as field separators, and hence not OK */
        if (strchr(p, ':'))
                return false;

        return true;
}
Beispiel #19
0
int change_uid_gid(const char *user, char **_home) {
        char line[LINE_MAX], *x, *u, *g, *h;
        const char *word, *state;
        _cleanup_free_ uid_t *uids = NULL;
        _cleanup_free_ char *home = NULL;
        _cleanup_fclose_ FILE *f = NULL;
        _cleanup_close_ int fd = -1;
        unsigned n_uids = 0;
        size_t sz = 0, l;
        uid_t uid;
        gid_t gid;
        pid_t pid;
        int r;

        assert(_home);

        if (!user || streq(user, "root") || streq(user, "0")) {
                /* Reset everything fully to 0, just in case */

                r = reset_uid_gid();
                if (r < 0)
                        return log_error_errno(r, "Failed to become root: %m");

                *_home = NULL;
                return 0;
        }

        /* First, get user credentials */
        fd = spawn_getent("passwd", user, &pid);
        if (fd < 0)
                return fd;

        f = fdopen(fd, "r");
        if (!f)
                return log_oom();
        fd = -1;

        if (!fgets(line, sizeof(line), f)) {

                if (!ferror(f)) {
                        log_error("Failed to resolve user %s.", user);
                        return -ESRCH;
                }

                log_error_errno(errno, "Failed to read from getent: %m");
                return -errno;
        }

        truncate_nl(line);

        wait_for_terminate_and_warn("getent passwd", pid, true);

        x = strchr(line, ':');
        if (!x) {
                log_error("/etc/passwd entry has invalid user field.");
                return -EIO;
        }

        u = strchr(x+1, ':');
        if (!u) {
                log_error("/etc/passwd entry has invalid password field.");
                return -EIO;
        }

        u++;
        g = strchr(u, ':');
        if (!g) {
                log_error("/etc/passwd entry has invalid UID field.");
                return -EIO;
        }

        *g = 0;
        g++;
        x = strchr(g, ':');
        if (!x) {
                log_error("/etc/passwd entry has invalid GID field.");
                return -EIO;
        }

        *x = 0;
        h = strchr(x+1, ':');
        if (!h) {
                log_error("/etc/passwd entry has invalid GECOS field.");
                return -EIO;
        }

        h++;
        x = strchr(h, ':');
        if (!x) {
                log_error("/etc/passwd entry has invalid home directory field.");
                return -EIO;
        }

        *x = 0;

        r = parse_uid(u, &uid);
        if (r < 0) {
                log_error("Failed to parse UID of user.");
                return -EIO;
        }

        r = parse_gid(g, &gid);
        if (r < 0) {
                log_error("Failed to parse GID of user.");
                return -EIO;
        }

        home = strdup(h);
        if (!home)
                return log_oom();

        /* Second, get group memberships */
        fd = spawn_getent("initgroups", user, &pid);
        if (fd < 0)
                return fd;

        fclose(f);
        f = fdopen(fd, "r");
        if (!f)
                return log_oom();
        fd = -1;

        if (!fgets(line, sizeof(line), f)) {
                if (!ferror(f)) {
                        log_error("Failed to resolve user %s.", user);
                        return -ESRCH;
                }

                log_error_errno(errno, "Failed to read from getent: %m");
                return -errno;
        }

        truncate_nl(line);

        wait_for_terminate_and_warn("getent initgroups", pid, true);

        /* Skip over the username and subsequent separator whitespace */
        x = line;
        x += strcspn(x, WHITESPACE);
        x += strspn(x, WHITESPACE);

        FOREACH_WORD(word, l, x, state) {
                char c[l+1];

                memcpy(c, word, l);
                c[l] = 0;

                if (!GREEDY_REALLOC(uids, sz, n_uids+1))
                        return log_oom();

                r = parse_uid(c, &uids[n_uids++]);
                if (r < 0) {
                        log_error("Failed to parse group data from getent.");
                        return -EIO;
                }
        }
Beispiel #20
0
/* auth_mysql_new_user_pass:
 * Attempt to authenticate a user via USER/PASS, using the template SELECT
 * query in the config file or the default defined above otherwise. */
authcontext auth_mysql_new_user_pass(const char *user, const char *local_part, const char *domain, const char *pass, const char *clienthost /* unused */, const char *serverhost) {
    char *query = NULL, *who;
    authcontext a = NULL;

    who = username_string(user, local_part, domain);
    
    if (!mysql_driver_active || !user_pass_query_template) return NULL;

    if (get_mysql_server() == -1) {
        log_print(LOG_ERR, _("auth_mysql_new_user_pass: aborting"));
        return NULL;
    }

    /* Obtain the actual query to use. */
    if (!(query = substitute_query_params(user_pass_query_template, user, local_part, domain, NULL, serverhost)))
        goto fail;

    if (verbose)
        log_print(LOG_DEBUG, _("auth_mysql_new_user_pass: SQL query: %s"), query);

    if (mysql_query(mysql, query) == 0) {
        MYSQL_RES *result;
        int i;

        result = mysql_store_result(mysql);
        if (!result) {
            log_print(LOG_ERR, _("auth_mysql_new_user_pass: mysql_store_result: %s"), mysql_error(mysql));
            goto fail;
        }

        if (mysql_field_count(mysql) != 4) {
            log_print(LOG_ERR, _("auth_mysql_new_user_pass: %d fields returned by query, should be 4: mailbox location, password hash, unix user, mailbox type"), mysql_field_count(mysql));
            goto fail;
        }

        switch (i = mysql_num_rows(result)) {
        case 0:
            break;
        case 1: {
                MYSQL_ROW row;
                unsigned long *lengths;
                struct passwd *pw;
                uid_t uid;
                
                row = mysql_fetch_row(result);

                /* These are "can't happen" errors */
                if (!row || !(lengths = mysql_fetch_lengths(result))) break;

                /* Sanity check. Verify that user has UID and password. */
                if (!row[2]) {
                    log_print(LOG_ERR, _("auth_mysql_new_user_pass: UID for user %s is NULL"), who);
                    goto fail;
                } else if (!row[1]) {
                    log_print(LOG_ERR, _("auth_mysql_new_user_pass: password hash for user %s is NULL"), who);
                    break;
                }

                /* Verify the password. */
                if (!check_password(who, row[1], pass, "{md5}")) {
                    log_print(LOG_ERR, _("auth_mysql_new_user_pass: %s failed login with wrong password"), who);
                    break;
                }

                if (!parse_uid((const char*)row[2], &uid)) {
                    log_print(LOG_ERR, _("auth_mysql_new_user_pass: unix user `%s' for %s does not make sense"), row[2], who);
                    break;
                }

                pw = getpwuid(uid);

                if (!pw) {
                    log_print(LOG_ERR, "auth_mysql_new_user_pass: getpwuid(%d): %m", (int)uid);
                    break;
                }

                a = authcontext_new(pw->pw_uid, use_gid ? mail_gid : pw->pw_gid, row[3], row[0], pw->pw_dir);
                break;
            }

        default:
            log_print(LOG_ERR, _("auth_mysql_new_user_pass: database inconsistency: query for %s returned %d rows, should be 0 or 1"), who, i);
            break;
        }

        mysql_free_result(result);
    } else
        log_print(LOG_ERR, "auth_mysql_new_user_pass: mysql_query: %s", mysql_error(mysql));

fail:
    xfree(query);

    return a;
}
Beispiel #21
0
/**
 * Parse permissions, including mount-time overrides. This is roughly the same behavior as
 * NTFS-3g, except that fmask and dmask override umask regardless of argument order.
 */
int permissions_setup(struct ltfs_fuse_data *priv)
{
	mode_t mode;

	/* Set defaults */
	priv->perm_override = false;
	priv->mount_uid = geteuid();
	priv->mount_gid = getegid();
	priv->file_mode = S_IFREG | 0777;
	priv->dir_mode = S_IFDIR | 0777;

	/* User ID override */
	if (priv->force_uid) {
		priv->perm_override = true;
		priv->mount_uid = parse_uid(priv->force_uid);
		if (priv->mount_uid == (uid_t)-1) {
			/* Invalid UID */
			ltfsmsg(LTFS_ERR, "14079E", priv->force_uid);
			return -1;
		}
		free(priv->force_uid);
	}

	/* Group ID override */
	if (priv->force_gid) {
		priv->perm_override = true;
		priv->mount_gid = parse_gid(priv->force_gid);
		if (priv->mount_gid == (gid_t)-1) {
			/* Invalid GID */
			ltfsmsg(LTFS_ERR, "14080E", priv->force_gid);
			return -1;
		}
		free(priv->force_gid);
	}

	/* Global (file and directory) permissions override */
	if (priv->force_umask) {
		priv->perm_override = true;
		mode = parse_mode(priv->force_umask);
		if (mode == (mode_t)-1) {
			/* Invalid umask */
			ltfsmsg(LTFS_ERR, "14006E", priv->force_umask);
			return -1;
		}
		priv->file_mode = (S_IFREG | 0777) & ~mode;
		priv->dir_mode = (S_IFDIR | 0777) & ~mode;
		free(priv->force_umask);
	}

	/* File permissions override */
	if (priv->force_fmask) {
		priv->perm_override = true;
		mode = parse_mode(priv->force_fmask);
		if (mode == (mode_t)-1) {
			/* Invalid fmask */
			ltfsmsg(LTFS_ERR, "14007E", priv->force_fmask);
			return -1;
		}
		priv->file_mode = (S_IFREG | 0777) & ~mode;
		free(priv->force_fmask);
	}

	/* Directory permissions override */
	if (priv->force_dmask) {
		priv->perm_override = true;
		mode = parse_mode(priv->force_dmask);
		if (mode == (mode_t)-1) {
			/* Invalid dmask */
			ltfsmsg(LTFS_ERR, "14008E", priv->force_dmask);
			return -1;
		}
		priv->dir_mode = (S_IFDIR | 0777) & ~mode;
		free(priv->force_dmask);
	}

/* Uncomment to apply the current umask to the default permissions, as vfat does.
	mode = umask(0);
	umask(mode);
	if (! priv->force_umask && ! priv->force_fmask)
		priv->file_mode = (S_IFREG | 0777) & ~mode;
	if (! priv->force_umask && ! priv->force_dmask)
		priv->dir_mode = (S_IFDIR | 0777) & ~mode;
*/

	return 0;
}
Beispiel #22
0
int get_user_creds(
                const char **username,
                uid_t *uid, gid_t *gid,
                const char **home,
                const char **shell) {

        struct passwd *p;
        uid_t u;

        assert(username);
        assert(*username);

        /* We enforce some special rules for uid=0: in order to avoid
         * NSS lookups for root we hardcode its data. */

        if (streq(*username, "root") || streq(*username, "0")) {
                *username = "******";

                if (uid)
                        *uid = 0;

                if (gid)
                        *gid = 0;

                if (home)
                        *home = "/root";

                if (shell)
                        *shell = "/bin/sh";

                return 0;
        }

        if (parse_uid(*username, &u) >= 0) {
                errno = 0;
                p = getpwuid(u);

                /* If there are multiple users with the same id, make
                 * sure to leave $USER to the configured value instead
                 * of the first occurrence in the database. However if
                 * the uid was configured by a numeric uid, then let's
                 * pick the real username from /etc/passwd. */
                if (p)
                        *username = p->pw_name;
        } else {
                errno = 0;
                p = getpwnam(*username);
        }

        if (!p)
                return errno > 0 ? -errno : -ESRCH;

        if (uid) {
                if (!uid_is_valid(p->pw_uid))
                        return -EBADMSG;

                *uid = p->pw_uid;
        }

        if (gid) {
                if (!gid_is_valid(p->pw_gid))
                        return -EBADMSG;

                *gid = p->pw_gid;
        }

        if (home)
                *home = p->pw_dir;

        if (shell)
                *shell = p->pw_shell;

        return 0;
}
Beispiel #23
0
int take_etc_passwd_lock(const char *root) {

        struct flock flock = {
                .l_type = F_WRLCK,
                .l_whence = SEEK_SET,
                .l_start = 0,
                .l_len = 0,
        };

        const char *path;
        int fd, r;

        /* This is roughly the same as lckpwdf(), but not as awful. We
         * don't want to use alarm() and signals, hence we implement
         * our own trivial version of this.
         *
         * Note that shadow-utils also takes per-database locks in
         * addition to lckpwdf(). However, we don't given that they
         * are redundant as they invoke lckpwdf() first and keep
         * it during everything they do. The per-database locks are
         * awfully racy, and thus we just won't do them. */

        if (root)
                path = prefix_roota(root, "/etc/.pwd.lock");
        else
                path = "/etc/.pwd.lock";

        fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
        if (fd < 0)
                return -errno;

        r = fcntl(fd, F_SETLKW, &flock);
        if (r < 0) {
                safe_close(fd);
                return -errno;
        }

        return fd;
}

bool valid_user_group_name(const char *u) {
        const char *i;
        long sz;

        /* Checks if the specified name is a valid user/group name. */

        if (isempty(u))
                return false;

        if (!(u[0] >= 'a' && u[0] <= 'z') &&
            !(u[0] >= 'A' && u[0] <= 'Z') &&
            u[0] != '_')
                return false;

        for (i = u+1; *i; i++) {
                if (!(*i >= 'a' && *i <= 'z') &&
                    !(*i >= 'A' && *i <= 'Z') &&
                    !(*i >= '0' && *i <= '9') &&
                    *i != '_' &&
                    *i != '-')
                        return false;
        }

        sz = sysconf(_SC_LOGIN_NAME_MAX);
        assert_se(sz > 0);

        if ((size_t) (i-u) > (size_t) sz)
                return false;

        if ((size_t) (i-u) > UT_NAMESIZE - 1)
                return false;

        return true;
}

bool valid_user_group_name_or_id(const char *u) {

        /* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the right
         * range, and not the invalid user ids. */

        if (isempty(u))
                return false;

        if (valid_user_group_name(u))
                return true;

        return parse_uid(u, NULL) >= 0;
}

bool valid_gecos(const char *d) {

        if (!d)
                return false;

        if (!utf8_is_valid(d))
                return false;

        if (string_has_cc(d, NULL))
                return false;

        /* Colons are used as field separators, and hence not OK */
        if (strchr(d, ':'))
                return false;

        return true;
}

bool valid_home(const char *p) {

        if (isempty(p))
                return false;

        if (!utf8_is_valid(p))
                return false;

        if (string_has_cc(p, NULL))
                return false;

        if (!path_is_absolute(p))
                return false;

        if (!path_is_safe(p))
                return false;

        /* Colons are used as field separators, and hence not OK */
        if (strchr(p, ':'))
                return false;

        return true;
}

int maybe_setgroups(size_t size, const gid_t *list) {
        int r;

        /* Check if setgroups is allowed before we try to drop all the auxiliary groups */
        if (size == 0) { /* Dropping all aux groups? */
                _cleanup_free_ char *setgroups_content = NULL;
                bool can_setgroups;

                r = read_one_line_file("/proc/self/setgroups", &setgroups_content);
                if (r == -ENOENT)
                        /* Old kernels don't have /proc/self/setgroups, so assume we can use setgroups */
                        can_setgroups = true;
                else if (r < 0)
                        return r;
                else
                        can_setgroups = streq(setgroups_content, "allow");

                if (!can_setgroups) {
                        log_debug("Skipping setgroups(), /proc/self/setgroups is set to 'deny'");
                        return 0;
                }
        }

        if (setgroups(size, list) < 0)
                return -errno;

        return 0;
}
Beispiel #24
0
int main(int argc, char *argv[])
{
	int ret;
	int c, dir_cnt = 0;
	int status;
	pid_t pid, rpid;

	program_name = strdup(argv[0]);
	if (!program_name) {
		printf("strdup() failed: %d (%m)\n", -errno);
		exit(EXIT_FAILURE);
	}

	program_name = basename(program_name);
	while ((c = getopt_long(argc, argv, "+hu:d:", options, NULL)) >= 0) {
		switch (c) {
		case 'h':
			help();
			return 0;

		case 'u':
			ret = parse_uid(optarg);
			if (ret < 0)
				return ret;

			break;

		default:
			break;
		}
	}

	if (getuid() != 0) {
		printf("%s: can't map arbitrary uids, test skipped.\n",
		       program_name);
		exit(EXIT_SUCCESS);
	}

	pid = syscall(__NR_clone, SIGCHLD|CLONE_NEWNS|
		      CLONE_MNTNS_SHIFT_UIDGID, NULL);
	if (pid < 0) {
		printf("clone() failed: %d (%m)\n", -errno);
		exit(EXIT_FAILURE);
	}

	if (pid == 0) {
		ret = prctl(PR_SET_PDEATHSIG, SIGKILL);
		if (ret < 0) {
			ret = -errno;
			printf("error prctl(): %d (%m)\n", ret);
			_exit(EXIT_FAILURE);
		}

		if (test_uidshift_mount() < 0) {
			printf("%s: uidshift mounting test failed.\n",
			       program_name);
			_exit(EXIT_FAILURE);
		}

		_exit(EXIT_SUCCESS);
	}

	rpid = waitpid(pid, &status, 0);
	if (rpid < 0) {
		ret = -errno;
		printf("waitpid() failed: %d (%m)\n", ret);
		exit(EXIT_FAILURE);
	}

	if (rpid != pid) {
		printf("waited for %d got %d\n", pid, rpid);
		exit(EXIT_FAILURE);
	}

	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
		printf("child did not terminate cleanly\n");
		exit(EXIT_FAILURE);
	}

	return EXIT_SUCCESS;
}
Beispiel #25
0
int main(int argc, char* argv[]) {

        /* The small core field we allocate on the stack, to keep things simple */
        char
                *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
                *core_session = NULL, *core_exe = NULL, *core_comm = NULL, *core_cmdline = NULL,
                *core_cgroup = NULL, *core_cwd = NULL, *core_root = NULL, *core_unit = NULL,
                *core_slice = NULL;

        /* The larger ones we allocate on the heap */
        _cleanup_free_ char
                *core_timestamp = NULL,  *core_message = NULL, *coredump_data = NULL, *core_owner_uid = NULL,
                *core_open_fds = NULL, *core_proc_status = NULL, *core_proc_maps = NULL, *core_proc_limits = NULL,
                *core_proc_cgroup = NULL, *core_environ = NULL;

        _cleanup_free_ char *exe = NULL, *comm = NULL, *filename = NULL;
        const char *info[_INFO_LEN];

        _cleanup_close_ int coredump_fd = -1;

        struct iovec iovec[26];
        uint64_t coredump_size;
        int r, j = 0;
        uid_t uid, owner_uid;
        gid_t gid;
        pid_t pid;
        char *t;
        const char *p;

        /* Make sure we never enter a loop */
        prctl(PR_SET_DUMPABLE, 0);

        /* First, log to a safe place, since we don't know what
         * crashed and it might be journald which we'd rather not log
         * to then. */
        log_set_target(LOG_TARGET_KMSG);
        log_open();

        if (argc < INFO_COMM + 1) {
                log_error("Not enough arguments passed from kernel (%d, expected %d).",
                          argc - 1, INFO_COMM + 1 - 1);
                r = -EINVAL;
                goto finish;
        }

        /* Ignore all parse errors */
        parse_config();

        log_debug("Selected storage '%s'.", coredump_storage_to_string(arg_storage));
        log_debug("Selected compression %s.", yes_no(arg_compress));

        r = parse_uid(argv[INFO_UID + 1], &uid);
        if (r < 0) {
                log_error("Failed to parse UID.");
                goto finish;
        }

        r = parse_pid(argv[INFO_PID + 1], &pid);
        if (r < 0) {
                log_error("Failed to parse PID.");
                goto finish;
        }

        r = parse_gid(argv[INFO_GID + 1], &gid);
        if (r < 0) {
                log_error("Failed to parse GID.");
                goto finish;
        }

        if (get_process_comm(pid, &comm) < 0) {
                log_warning("Failed to get COMM, falling back to the command line.");
                comm = strv_join(argv + INFO_COMM + 1, " ");
        }

        if (get_process_exe(pid, &exe) < 0)
                log_warning("Failed to get EXE.");

        info[INFO_PID] = argv[INFO_PID + 1];
        info[INFO_UID] = argv[INFO_UID + 1];
        info[INFO_GID] = argv[INFO_GID + 1];
        info[INFO_SIGNAL] = argv[INFO_SIGNAL + 1];
        info[INFO_TIMESTAMP] = argv[INFO_TIMESTAMP + 1];
        info[INFO_COMM] = comm;
        info[INFO_EXE] = exe;

        if (cg_pid_get_unit(pid, &t) >= 0) {

                if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
                        free(t);

                        /* If we are journald, we cut things short,
                         * don't write to the journal, but still
                         * create a coredump. */

                        if (arg_storage != COREDUMP_STORAGE_NONE)
                                arg_storage = COREDUMP_STORAGE_EXTERNAL;

                        r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
                        if (r < 0)
                                goto finish;

                        r = maybe_remove_external_coredump(filename, coredump_size);
                        if (r < 0)
                                goto finish;

                        log_info("Detected coredump of the journal daemon itself, diverted to %s.", filename);
                        goto finish;
                }

                core_unit = strjoina("COREDUMP_UNIT=", t);
                free(t);

        } else if (cg_pid_get_user_unit(pid, &t) >= 0) {
                core_unit = strjoina("COREDUMP_USER_UNIT=", t);
                free(t);
        }

        if (core_unit)
                IOVEC_SET_STRING(iovec[j++], core_unit);

        /* OK, now we know it's not the journal, hence we can make use
         * of it now. */
        log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
        log_open();

        core_pid = strjoina("COREDUMP_PID=", info[INFO_PID]);
        IOVEC_SET_STRING(iovec[j++], core_pid);

        core_uid = strjoina("COREDUMP_UID=", info[INFO_UID]);
        IOVEC_SET_STRING(iovec[j++], core_uid);

        core_gid = strjoina("COREDUMP_GID=", info[INFO_GID]);
        IOVEC_SET_STRING(iovec[j++], core_gid);

        core_signal = strjoina("COREDUMP_SIGNAL=", info[INFO_SIGNAL]);
        IOVEC_SET_STRING(iovec[j++], core_signal);

        if (sd_pid_get_session(pid, &t) >= 0) {
                core_session = strjoina("COREDUMP_SESSION=", t);
                free(t);

                IOVEC_SET_STRING(iovec[j++], core_session);
        }

        if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
                r = asprintf(&core_owner_uid,
                             "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
                if (r > 0)
                        IOVEC_SET_STRING(iovec[j++], core_owner_uid);
        }

        if (sd_pid_get_slice(pid, &t) >= 0) {
                core_slice = strjoina("COREDUMP_SLICE=", t);
                free(t);

                IOVEC_SET_STRING(iovec[j++], core_slice);
        }

        if (comm) {
                core_comm = strjoina("COREDUMP_COMM=", comm);
                IOVEC_SET_STRING(iovec[j++], core_comm);
        }

        if (exe) {
                core_exe = strjoina("COREDUMP_EXE=", exe);
                IOVEC_SET_STRING(iovec[j++], core_exe);
        }

        if (get_process_cmdline(pid, 0, false, &t) >= 0) {
                core_cmdline = strjoina("COREDUMP_CMDLINE=", t);
                free(t);

                IOVEC_SET_STRING(iovec[j++], core_cmdline);
        }

        if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) {
                core_cgroup = strjoina("COREDUMP_CGROUP=", t);
                free(t);

                IOVEC_SET_STRING(iovec[j++], core_cgroup);
        }

        if (compose_open_fds(pid, &t) >= 0) {
                core_open_fds = strappend("COREDUMP_OPEN_FDS=", t);
                free(t);

                if (core_open_fds)
                        IOVEC_SET_STRING(iovec[j++], core_open_fds);
        }

        p = procfs_file_alloca(pid, "status");
        if (read_full_file(p, &t, NULL) >= 0) {
                core_proc_status = strappend("COREDUMP_PROC_STATUS=", t);
                free(t);

                if (core_proc_status)
                        IOVEC_SET_STRING(iovec[j++], core_proc_status);
        }

        p = procfs_file_alloca(pid, "maps");
        if (read_full_file(p, &t, NULL) >= 0) {
                core_proc_maps = strappend("COREDUMP_PROC_MAPS=", t);
                free(t);

                if (core_proc_maps)
                        IOVEC_SET_STRING(iovec[j++], core_proc_maps);
        }

        p = procfs_file_alloca(pid, "limits");
        if (read_full_file(p, &t, NULL) >= 0) {
                core_proc_limits = strappend("COREDUMP_PROC_LIMITS=", t);
                free(t);

                if (core_proc_limits)
                        IOVEC_SET_STRING(iovec[j++], core_proc_limits);
        }

        p = procfs_file_alloca(pid, "cgroup");
        if (read_full_file(p, &t, NULL) >=0) {
                core_proc_cgroup = strappend("COREDUMP_PROC_CGROUP=", t);
                free(t);

                if (core_proc_cgroup)
                        IOVEC_SET_STRING(iovec[j++], core_proc_cgroup);
        }

        if (get_process_cwd(pid, &t) >= 0) {
                core_cwd = strjoina("COREDUMP_CWD=", t);
                free(t);

                IOVEC_SET_STRING(iovec[j++], core_cwd);
        }

        if (get_process_root(pid, &t) >= 0) {
                core_root = strjoina("COREDUMP_ROOT=", t);
                free(t);

                IOVEC_SET_STRING(iovec[j++], core_root);
        }

        if (get_process_environ(pid, &t) >= 0) {
                core_environ = strappend("COREDUMP_ENVIRON=", t);
                free(t);

                if (core_environ)
                        IOVEC_SET_STRING(iovec[j++], core_environ);
        }

        core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL);
        if (core_timestamp)
                IOVEC_SET_STRING(iovec[j++], core_timestamp);

        IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
        IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");

        /* Vacuum before we write anything again */
        coredump_vacuum(-1, arg_keep_free, arg_max_use);

        /* Always stream the coredump to disk, if that's possible */
        r = save_external_coredump(info, uid, &filename, &coredump_fd, &coredump_size);
        if (r < 0)
                /* skip whole core dumping part */
                goto log;

        /* If we don't want to keep the coredump on disk, remove it
         * now, as later on we will lack the privileges for
         * it. However, we keep the fd to it, so that we can still
         * process it and log it. */
        r = maybe_remove_external_coredump(filename, coredump_size);
        if (r < 0)
                goto finish;
        if (r == 0) {
                const char *coredump_filename;

                coredump_filename = strjoina("COREDUMP_FILENAME=", filename);
                IOVEC_SET_STRING(iovec[j++], coredump_filename);
        }

        /* Vacuum again, but exclude the coredump we just created */
        coredump_vacuum(coredump_fd, arg_keep_free, arg_max_use);

        /* Now, let's drop privileges to become the user who owns the
         * segfaulted process and allocate the coredump memory under
         * the user's uid. This also ensures that the credentials
         * journald will see are the ones of the coredumping user,
         * thus making sure the user gets access to the core
         * dump. Let's also get rid of all capabilities, if we run as
         * root, we won't need them anymore. */
        r = drop_privileges(uid, gid, 0);
        if (r < 0) {
                log_error_errno(r, "Failed to drop privileges: %m");
                goto finish;
        }

#ifdef HAVE_ELFUTILS
        /* Try to get a strack trace if we can */
        if (coredump_size <= arg_process_size_max) {
                _cleanup_free_ char *stacktrace = NULL;

                r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
                if (r >= 0)
                        core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.\n\n", stacktrace, NULL);
                else if (r == -EINVAL)
                        log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
                else
                        log_warning_errno(r, "Failed to generate stack trace: %m");
        }

        if (!core_message)
#endif
log:
        core_message = strjoin("MESSAGE=Process ", info[INFO_PID], " (", comm, ") of user ", info[INFO_UID], " dumped core.", NULL);
        if (core_message)
                IOVEC_SET_STRING(iovec[j++], core_message);

        /* Optionally store the entire coredump in the journal */
        if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
            coredump_size <= arg_journal_size_max) {
                size_t sz = 0;

                /* Store the coredump itself in the journal */

                r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
                if (r >= 0) {
                        iovec[j].iov_base = coredump_data;
                        iovec[j].iov_len = sz;
                        j++;
                }
        }

        r = sd_journal_sendv(iovec, j);
        if (r < 0)
                log_error_errno(r, "Failed to log coredump: %m");

finish:
        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
Beispiel #26
0
static int save_external_coredump(
                const char *context[_CONTEXT_MAX],
                int input_fd,
                char **ret_filename,
                int *ret_node_fd,
                int *ret_data_fd,
                uint64_t *ret_size) {

        _cleanup_free_ char *fn = NULL, *tmp = NULL;
        _cleanup_close_ int fd = -1;
        uint64_t rlimit, max_size;
        struct stat st;
        uid_t uid;
        int r;

        assert(context);
        assert(ret_filename);
        assert(ret_node_fd);
        assert(ret_data_fd);
        assert(ret_size);

        r = parse_uid(context[CONTEXT_UID], &uid);
        if (r < 0)
                return log_error_errno(r, "Failed to parse UID: %m");

        r = safe_atou64(context[CONTEXT_RLIMIT], &rlimit);
        if (r < 0)
                return log_error_errno(r, "Failed to parse resource limit: %s", context[CONTEXT_RLIMIT]);
        if (rlimit <= 0) {
                /* Is coredumping disabled? Then don't bother saving/processing the coredump */
                log_info("Core Dumping has been disabled for process %s (%s).", context[CONTEXT_PID], context[CONTEXT_COMM]);
                return -EBADSLT;
        }

        /* Never store more than the process configured, or than we actually shall keep or process */
        max_size = MIN(rlimit, MAX(arg_process_size_max, arg_external_size_max));

        r = make_filename(context, &fn);
        if (r < 0)
                return log_error_errno(r, "Failed to determine coredump file name: %m");

        mkdir_p_label("/var/lib/systemd/coredump", 0755);

        fd = open_tmpfile_linkable(fn, O_RDWR|O_CLOEXEC, &tmp);
        if (fd < 0)
                return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn);

        r = copy_bytes(input_fd, fd, max_size, false);
        if (r == -EFBIG) {
                log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", context[CONTEXT_PID], context[CONTEXT_COMM]);
                goto fail;
        } else if (IN_SET(r, -EDQUOT, -ENOSPC)) {
                log_error("Not enough disk space for coredump of %s (%s), refusing.", context[CONTEXT_PID], context[CONTEXT_COMM]);
                goto fail;
        } else if (r < 0) {
                log_error_errno(r, "Failed to dump coredump to file: %m");
                goto fail;
        }

        if (fstat(fd, &st) < 0) {
                log_error_errno(errno, "Failed to fstat coredump %s: %m", coredump_tmpfile_name(tmp));
                goto fail;
        }

        if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
                log_error_errno(errno, "Failed to seek on %s: %m", coredump_tmpfile_name(tmp));
                goto fail;
        }

#if defined(HAVE_XZ) || defined(HAVE_LZ4)
        /* If we will remove the coredump anyway, do not compress. */
        if (maybe_remove_external_coredump(NULL, st.st_size) == 0
            && arg_compress) {

                _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL;
                _cleanup_close_ int fd_compressed = -1;

                fn_compressed = strappend(fn, COMPRESSED_EXT);
                if (!fn_compressed) {
                        log_oom();
                        goto uncompressed;
                }

                fd_compressed = open_tmpfile_linkable(fn_compressed, O_RDWR|O_CLOEXEC, &tmp_compressed);
                if (fd_compressed < 0) {
                        log_error_errno(fd_compressed, "Failed to create temporary file for coredump %s: %m", fn_compressed);
                        goto uncompressed;
                }

                r = compress_stream(fd, fd_compressed, -1);
                if (r < 0) {
                        log_error_errno(r, "Failed to compress %s: %m", coredump_tmpfile_name(tmp_compressed));
                        goto fail_compressed;
                }

                r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid);
                if (r < 0)
                        goto fail_compressed;

                /* OK, this worked, we can get rid of the uncompressed version now */
                if (tmp)
                        unlink_noerrno(tmp);

                *ret_filename = fn_compressed;     /* compressed */
                *ret_node_fd = fd_compressed;      /* compressed */
                *ret_data_fd = fd;                 /* uncompressed */
                *ret_size = (uint64_t) st.st_size; /* uncompressed */

                fn_compressed = NULL;
                fd = fd_compressed = -1;

                return 0;

        fail_compressed:
                if (tmp_compressed)
                        (void) unlink(tmp_compressed);
        }

uncompressed:
#endif

        r = fix_permissions(fd, tmp, fn, context, uid);
        if (r < 0)
                goto fail;

        *ret_filename = fn;
        *ret_data_fd = fd;
        *ret_node_fd = -1;
        *ret_size = (uint64_t) st.st_size;

        fn = NULL;
        fd = -1;

        return 0;

fail:
        if (tmp)
                (void) unlink(tmp);
        return r;
}
static int save_external_coredump(
                const char *context[_CONTEXT_MAX],
                int input_fd,
                char **ret_filename,
                int *ret_node_fd,
                int *ret_data_fd,
                uint64_t *ret_size) {

        _cleanup_free_ char *fn = NULL, *tmp = NULL;
        _cleanup_close_ int fd = -1;
        uint64_t rlimit, max_size;
        struct stat st;
        uid_t uid;
        int r;

        assert(context);
        assert(ret_filename);
        assert(ret_node_fd);
        assert(ret_data_fd);
        assert(ret_size);

        r = parse_uid(context[CONTEXT_UID], &uid);
        if (r < 0)
                return log_error_errno(r, "Failed to parse UID: %m");

        r = safe_atou64(context[CONTEXT_RLIMIT], &rlimit);
        if (r < 0)
                return log_error_errno(r, "Failed to parse resource limit: %s", context[CONTEXT_RLIMIT]);
        if (rlimit < page_size()) {
                /* Is coredumping disabled? Then don't bother saving/processing the coredump.
                 * Anything below PAGE_SIZE cannot give a readable coredump (the kernel uses
                 * ELF_EXEC_PAGESIZE which is not easily accessible, but is usually the same as PAGE_SIZE. */
                log_info("Resource limits disable core dumping for process %s (%s).",
                         context[CONTEXT_PID], context[CONTEXT_COMM]);
                return -EBADSLT;
        }

        /* Never store more than the process configured, or than we actually shall keep or process */
        max_size = MIN(rlimit, MAX(arg_process_size_max, storage_size_max()));

        r = make_filename(context, &fn);
        if (r < 0)
                return log_error_errno(r, "Failed to determine coredump file name: %m");

        mkdir_p_label("/var/lib/systemd/coredump", 0755);

        fd = open_tmpfile_linkable(fn, O_RDWR|O_CLOEXEC, &tmp);
        if (fd < 0)
                return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn);

        r = copy_bytes(input_fd, fd, max_size, false);
        if (r < 0) {
                log_error_errno(r, "Cannot store coredump of %s (%s): %m", context[CONTEXT_PID], context[CONTEXT_COMM]);
                goto fail;
        } else if (r == 1)
                log_struct(LOG_INFO,
                           LOG_MESSAGE("Core file was truncated to %zu bytes.", max_size),
                           "SIZE_LIMIT=%zu", max_size,
                           LOG_MESSAGE_ID(SD_MESSAGE_TRUNCATED_CORE),
                           NULL);

        if (fstat(fd, &st) < 0) {
                log_error_errno(errno, "Failed to fstat core file %s: %m", coredump_tmpfile_name(tmp));
                goto fail;
        }

        if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
                log_error_errno(errno, "Failed to seek on %s: %m", coredump_tmpfile_name(tmp));
                goto fail;
        }

#if defined(HAVE_XZ) || defined(HAVE_LZ4)
        /* If we will remove the coredump anyway, do not compress. */
        if (arg_compress && !maybe_remove_external_coredump(NULL, st.st_size)) {

                _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL;
                _cleanup_close_ int fd_compressed = -1;

                fn_compressed = strappend(fn, COMPRESSED_EXT);
                if (!fn_compressed) {
                        log_oom();
                        goto uncompressed;
                }

                fd_compressed = open_tmpfile_linkable(fn_compressed, O_RDWR|O_CLOEXEC, &tmp_compressed);
                if (fd_compressed < 0) {
                        log_error_errno(fd_compressed, "Failed to create temporary file for coredump %s: %m", fn_compressed);
                        goto uncompressed;
                }

                r = compress_stream(fd, fd_compressed, -1);
                if (r < 0) {
                        log_error_errno(r, "Failed to compress %s: %m", coredump_tmpfile_name(tmp_compressed));
                        goto fail_compressed;
                }

                r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid);
                if (r < 0)
                        goto fail_compressed;

                /* OK, this worked, we can get rid of the uncompressed version now */
                if (tmp)
                        unlink_noerrno(tmp);

                *ret_filename = fn_compressed;     /* compressed */
                *ret_node_fd = fd_compressed;      /* compressed */
                *ret_data_fd = fd;                 /* uncompressed */
                *ret_size = (uint64_t) st.st_size; /* uncompressed */

                fn_compressed = NULL;
                fd = fd_compressed = -1;

                return 0;

        fail_compressed:
                if (tmp_compressed)
                        (void) unlink(tmp_compressed);
        }

uncompressed:
#endif

        r = fix_permissions(fd, tmp, fn, context, uid);
        if (r < 0)
                goto fail;

        *ret_filename = fn;
        *ret_data_fd = fd;
        *ret_node_fd = -1;
        *ret_size = (uint64_t) st.st_size;

        fn = NULL;
        fd = -1;

        return 0;

fail:
        if (tmp)
                (void) unlink(tmp);
        return r;
}
Beispiel #28
0
int main(int argc, char* argv[]) {
        int r, j = 0;
        _cleanup_free_ char *p = NULL;
        ssize_t n;
        pid_t pid;
        uid_t uid;
        gid_t gid;
        struct iovec iovec[14];
        _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
                *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
                *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *t = NULL;

        prctl(PR_SET_DUMPABLE, 0);

        if (argc != _ARG_MAX) {
                log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
                log_open();

                log_error("Invalid number of arguments passed from kernel.");
                r = -EINVAL;
                goto finish;
        }

        r = parse_pid(argv[ARG_PID], &pid);
        if (r < 0) {
                log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
                log_open();

                log_error("Failed to parse PID.");
                goto finish;
        }

        if (cg_pid_get_unit(pid, &t) >= 0) {

                if (streq(t, SPECIAL_JOURNALD_SERVICE)) {
                        /* Make sure we don't make use of the journal,
                         * if it's the journal which is crashing */
                        log_set_target(LOG_TARGET_KMSG);
                        log_open();

                        r = divert_coredump();
                        goto finish;
                }

                core_unit = strappend("COREDUMP_UNIT=", t);
        } else if (cg_pid_get_user_unit(pid, &t) >= 0)
                core_unit = strappend("COREDUMP_USER_UNIT=", t);

        if (core_unit)
                IOVEC_SET_STRING(iovec[j++], core_unit);

        /* OK, now we know it's not the journal, hence make use of
         * it */
        log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
        log_open();

        r = parse_uid(argv[ARG_UID], &uid);
        if (r < 0) {
                log_error("Failed to parse UID.");
                goto finish;
        }

        r = parse_gid(argv[ARG_GID], &gid);
        if (r < 0) {
                log_error("Failed to parse GID.");
                goto finish;
        }

        core_pid = strappend("COREDUMP_PID=", argv[ARG_PID]);
        if (core_pid)
                IOVEC_SET_STRING(iovec[j++], core_pid);

        core_uid = strappend("COREDUMP_UID=", argv[ARG_UID]);
        if (core_uid)
                IOVEC_SET_STRING(iovec[j++], core_uid);

        core_gid = strappend("COREDUMP_GID=", argv[ARG_GID]);
        if (core_gid)
                IOVEC_SET_STRING(iovec[j++], core_gid);

        core_signal = strappend("COREDUMP_SIGNAL=", argv[ARG_SIGNAL]);
        if (core_signal)
                IOVEC_SET_STRING(iovec[j++], core_signal);

        core_comm = strappend("COREDUMP_COMM=", argv[ARG_COMM]);
        if (core_comm)
                IOVEC_SET_STRING(iovec[j++], core_comm);

#ifdef HAVE_LOGIND
        if (sd_pid_get_session(pid, &t) >= 0) {
                core_session = strappend("COREDUMP_SESSION=", t);
                free(t);

                if (core_session)
                        IOVEC_SET_STRING(iovec[j++], core_session);
        }

#endif

        if (get_process_exe(pid, &t) >= 0) {
                core_exe = strappend("COREDUMP_EXE=", t);
                free(t);

                if (core_exe)
                        IOVEC_SET_STRING(iovec[j++], core_exe);
        }

        if (get_process_cmdline(pid, 0, false, &t) >= 0) {
                core_cmdline = strappend("COREDUMP_CMDLINE=", t);
                free(t);

                if (core_cmdline)
                        IOVEC_SET_STRING(iovec[j++], core_cmdline);
        }

        core_timestamp = strjoin("COREDUMP_TIMESTAMP=", argv[ARG_TIMESTAMP], "000000", NULL);
        if (core_timestamp)
                IOVEC_SET_STRING(iovec[j++], core_timestamp);

        IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
        IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");

        core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") dumped core.", NULL);
        if (core_message)
                IOVEC_SET_STRING(iovec[j++], core_message);

        /* Now, let's drop privileges to become the user who owns the
         * segfaulted process and allocate the coredump memory under
         * his uid. This also ensures that the credentials journald
         * will see are the ones of the coredumping user, thus making
         * sure the user himself gets access to the core dump. */

        if (setresgid(gid, gid, gid) < 0 ||
            setresuid(uid, uid, uid) < 0) {
                log_error("Failed to drop privileges: %m");
                r = -errno;
                goto finish;
        }

        p = malloc(9 + COREDUMP_MAX);
        if (!p) {
                r = log_oom();
                goto finish;
        }

        memcpy(p, "COREDUMP=", 9);

        n = loop_read(STDIN_FILENO, p + 9, COREDUMP_MAX, false);
        if (n < 0) {
                log_error("Failed to read core dump data: %s", strerror(-n));
                r = (int) n;
                goto finish;
        }

        iovec[j].iov_base = p;
        iovec[j].iov_len = 9 + n;
        j++;

        r = sd_journal_sendv(iovec, j);
        if (r < 0)
                log_error("Failed to send coredump: %s", strerror(-r));

finish:
        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
int config_parse_private_users(
                const char *unit,
                const char *filename,
                unsigned line,
                const char *section,
                unsigned section_line,
                const char *lvalue,
                int ltype,
                const char *rvalue,
                void *data,
                void *userdata) {

        Settings *settings = data;
        int r;

        assert(filename);
        assert(lvalue);
        assert(rvalue);

        r = parse_boolean(rvalue);
        if (r == 0) {
                /* no: User namespacing off */
                settings->userns_mode = USER_NAMESPACE_NO;
                settings->uid_shift = UID_INVALID;
                settings->uid_range = UINT32_C(0x10000);
        } else if (r > 0) {
                /* yes: User namespacing on, UID range is read from root dir */
                settings->userns_mode = USER_NAMESPACE_FIXED;
                settings->uid_shift = UID_INVALID;
                settings->uid_range = UINT32_C(0x10000);
        } else if (streq(rvalue, "pick")) {
                /* pick: User namespacing on, UID range is picked randomly */
                settings->userns_mode = USER_NAMESPACE_PICK;
                settings->uid_shift = UID_INVALID;
                settings->uid_range = UINT32_C(0x10000);
        } else {
                const char *range, *shift;
                uid_t sh, rn;

                /* anything else: User namespacing on, UID range is explicitly configured */

                range = strchr(rvalue, ':');
                if (range) {
                        shift = strndupa(rvalue, range - rvalue);
                        range++;

                        r = safe_atou32(range, &rn);
                        if (r < 0 || rn <= 0) {
                                log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID range invalid, ignoring: %s", range);
                                return 0;
                        }
                } else {
                        shift = rvalue;
                        rn = UINT32_C(0x10000);
                }

                r = parse_uid(shift, &sh);
                if (r < 0) {
                        log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID shift invalid, ignoring: %s", range);
                        return 0;
                }

                settings->userns_mode = USER_NAMESPACE_FIXED;
                settings->uid_shift = sh;
                settings->uid_range = rn;
        }

        return 0;
}
Beispiel #30
0
int 
main(int argc, char *argv[])
{
	struct iovec	*iov;
	int ch, iovlen;
	char source [MAXPATHLEN], target[MAXPATHLEN], errmsg[255];
	char uid_str[20], gid_str[20];
	char fstype[] = "unionfs";
	char *p, *val;

	iov = NULL;
	iovlen = 0;
	memset(errmsg, 0, sizeof(errmsg));

	while ((ch = getopt(argc, argv, "bo:")) != -1) {
		switch (ch) {
		case 'b':
			printf("\n  -b is deprecated.  Use \"-o below\" instead\n");
			build_iovec(&iov, &iovlen, "below", NULL, 0);
			break;
		case 'o':
                        p = strchr(optarg, '=');
                        val = NULL;
                        if (p != NULL) {
                                *p = '\0';
                                val = p + 1;
				if (strcmp(optarg, "gid") == 0) {
					parse_gid(val, gid_str, sizeof(gid_str));
					val = gid_str;
				}
				else if (strcmp(optarg, "uid") == 0) {
					parse_uid(val, uid_str, sizeof(uid_str));
					val = uid_str;
				}
                        }
                        build_iovec(&iov, &iovlen, optarg, val, (size_t)-1);
			break;
		case '?':
		default:
			usage();
			/* NOTREACHED */
		}
	}
	argc -= optind;
	argv += optind;

	if (argc != 2)
		usage();

	/* resolve both target and source with realpath(3) */
	if (checkpath(argv[0], target) != 0)
		err(EX_USAGE, "%s", target);
	if (checkpath(argv[1], source) != 0)
		err(EX_USAGE, "%s", source);

	if (subdir(target, source) || subdir(source, target))
		errx(EX_USAGE, "%s (%s) and %s (%s) are not distinct paths",
		     argv[0], target, argv[1], source);

	build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1);
	build_iovec(&iov, &iovlen, "fspath", source, (size_t)-1);
	build_iovec(&iov, &iovlen, "from", target, (size_t)-1);
	build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));

	if (nmount(iov, iovlen, 0))
		err(EX_OSERR, "%s: %s", source, errmsg);
	exit(0);
}