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)); }
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); }
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; }
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; }
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; }
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; }
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)); }
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); }
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; }
_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); }
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); }
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; }
/* * 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); }
/* 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; }
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); }
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 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; } }
/* 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; }
/** * 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; }
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; }
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; }
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; }
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; }
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; }
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; }
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); }