|
| 1 | +#include <einfo.h> |
| 2 | +#include <errno.h> |
| 3 | +#include <fcntl.h> |
| 4 | +#include <grp.h> |
| 5 | +#include <pwd.h> |
| 6 | +#include <stdlib.h> |
| 7 | +#include <sys/types.h> |
| 8 | +#include <sys/wait.h> |
| 9 | +#include <syslog.h> |
| 10 | +#include <unistd.h> |
| 11 | +#include <signal.h> |
| 12 | + |
| 13 | +#ifdef HAVE_PAM |
| 14 | +#include <security/pam_appl.h> |
| 15 | +static struct pam_conv conv = { NULL, NULL }; |
| 16 | +static pam_handle_t *pamh = NULL; |
| 17 | +#endif |
| 18 | + |
| 19 | +#include "helpers.h" |
| 20 | +#include "rc.h" |
| 21 | + |
| 22 | +enum setup_mode { |
| 23 | + CLIENT, |
| 24 | + SERVER, |
| 25 | + FAILED |
| 26 | +}; |
| 27 | + |
| 28 | +static const struct passwd *user; |
| 29 | +static char *fifopath; |
| 30 | +static size_t logins; |
| 31 | + |
| 32 | + |
| 33 | +static void cleanup(void) { |
| 34 | +#ifdef HAVE_PAM |
| 35 | + if (pamh) { |
| 36 | + int rc; |
| 37 | + if ((rc = pam_close_session(pamh, PAM_SILENT)) != PAM_SUCCESS) |
| 38 | + elog(LOG_ERR, "Failed to close session: %s", pam_strerror(pamh, rc)); |
| 39 | + pam_end(pamh, rc); |
| 40 | + } |
| 41 | +#endif |
| 42 | + |
| 43 | + if (exists(fifopath)) |
| 44 | + unlink(fifopath); |
| 45 | + free(fifopath); |
| 46 | +} |
| 47 | + |
| 48 | +static enum setup_mode do_setup(const char *username) { |
| 49 | + char *logname; |
| 50 | + int nullfd; |
| 51 | + |
| 52 | + xasprintf(&logname, "openrc-user[%s]", username); |
| 53 | + setenv("EINFO_LOG", logname, true); |
| 54 | + free(logname); |
| 55 | + |
| 56 | + if (!(user = getpwnam(username))) { |
| 57 | + elog(LOG_ERR, "getpwnam failed: %s", strerror(errno)); |
| 58 | + return false; |
| 59 | + } |
| 60 | + |
| 61 | + nullfd = open("/dev/null", O_RDWR); |
| 62 | + dup2(nullfd, STDIN_FILENO); |
| 63 | + close(nullfd); |
| 64 | + |
| 65 | + xasprintf(&fifopath, "%s/users/%s", rc_svcdir(), user->pw_name); |
| 66 | + if (mkfifo(fifopath, 0600) == -1 && errno != EEXIST) { |
| 67 | + elog(LOG_ERR, "mkfifo failed: %s", strerror(errno)); |
| 68 | + return FAILED; |
| 69 | + } |
| 70 | + |
| 71 | + return errno == EEXIST ? CLIENT : SERVER; |
| 72 | +} |
| 73 | + |
| 74 | +static void do_openrc(bool start) { |
| 75 | + pid_t child; |
| 76 | + char *cmd; |
| 77 | + |
| 78 | + switch ((child = fork())) { |
| 79 | + case 0: |
| 80 | + if (setgid(user->pw_gid) == -1 || setuid(user->pw_uid) == -1) { |
| 81 | + elog(LOG_ERR, "Failed to drop permissions to user."); |
| 82 | + exit(1); |
| 83 | + } |
| 84 | + |
| 85 | + setenv("HOME", user->pw_dir, true); |
| 86 | + setenv("SHELL", user->pw_shell, true); |
| 87 | + |
| 88 | + xasprintf(&cmd, "%s %s", RC_LIBEXECDIR "/sh/openrc-user.sh", start ? "start" : "stop"); |
| 89 | + execl(user->pw_shell, "-", "-c", cmd, NULL); |
| 90 | + |
| 91 | + elog(LOG_ERR, "Failed to execl '%s - -c %s': %s.", user->pw_shell, cmd, strerror(errno)); |
| 92 | + exit(1); |
| 93 | + case -1: |
| 94 | + exit(1); |
| 95 | + default: |
| 96 | + break; |
| 97 | + } |
| 98 | + |
| 99 | + waitpid(child, NULL, 0); |
| 100 | +} |
| 101 | + |
| 102 | +static void open_session(void) { |
| 103 | +#ifdef HAVE_PAM |
| 104 | + bool pam_session = false; |
| 105 | + int rc; |
| 106 | + |
| 107 | + if ((rc = pam_start("openrc-user", user->pw_name, &conv, &pamh)) != PAM_SUCCESS) |
| 108 | + elog(LOG_ERR, "Failed to start pam: %s", pam_strerror(pamh, rc)); |
| 109 | + else if ((rc = pam_open_session(pamh, PAM_SILENT)) != PAM_SUCCESS) |
| 110 | + elog(LOG_ERR, "Failed to open session: %s", pam_strerror(pamh, rc)); |
| 111 | + else |
| 112 | + pam_session = true; |
| 113 | + |
| 114 | + for (char **env = pam_getenvlist(pamh); env && *env; env++) { |
| 115 | + if (strchr(*env, '=')) |
| 116 | + putenv(xstrdup(*env)); |
| 117 | + else |
| 118 | + unsetenv(*env); |
| 119 | + } |
| 120 | + |
| 121 | + if (!pam_session && pamh) |
| 122 | + pam_end(pamh, rc); |
| 123 | +#endif |
| 124 | + |
| 125 | + return; |
| 126 | +} |
| 127 | + |
| 128 | +int main(int argc, char **argv) { |
| 129 | + char *username, *cmd, *service; |
| 130 | + FILE *fifo; |
| 131 | + |
| 132 | + /* Avoid recursing pam stacks */ |
| 133 | + if ((service = getenv("PAM_SERVICE")) && strcmp(service, "openc-user")) |
| 134 | + return 0; |
| 135 | + |
| 136 | + switch (argc) { |
| 137 | + case 3: |
| 138 | + username = argv[1]; |
| 139 | + cmd = argv[2]; |
| 140 | + break; |
| 141 | + case 2: |
| 142 | + cmd = argv[1]; |
| 143 | + if ((username = getenv("PAM_USER"))) |
| 144 | + break; |
| 145 | + /* falls through */ |
| 146 | + default: |
| 147 | + fprintf(stderr, "%s: Not enough arguments.\n", argv[0]); |
| 148 | + return 1; |
| 149 | + } |
| 150 | + |
| 151 | + switch (do_setup(username)) { |
| 152 | + case FAILED: |
| 153 | + return 1; |
| 154 | + case CLIENT: |
| 155 | + fifo = fopen(fifopath, "w"); |
| 156 | + fputs(cmd, fifo); |
| 157 | + free(fifopath); |
| 158 | + fclose(fifo); |
| 159 | + return 0; |
| 160 | + case SERVER: |
| 161 | + setsid(); |
| 162 | + if (fork() != 0) |
| 163 | + return 0; |
| 164 | + break; |
| 165 | + } |
| 166 | + |
| 167 | + fifo = fopen(fifopath, "r"); |
| 168 | + |
| 169 | + atexit(cleanup); |
| 170 | + |
| 171 | + /* we need to initgroups before pam is setup. */ |
| 172 | + if (initgroups(user->pw_name, user->pw_gid) == -1) |
| 173 | + return 1; |
| 174 | + |
| 175 | + open_session(); |
| 176 | + |
| 177 | + do_openrc(true); |
| 178 | + |
| 179 | + for (;;) { |
| 180 | + char buf[BUFSIZ]; |
| 181 | + size_t count; |
| 182 | + |
| 183 | + if (!(fifo = fopen(fifopath, "r"))) { |
| 184 | + if (errno != EINTR) |
| 185 | + elog(LOG_ERR, "fopen failed: %s", strerror(errno)); |
| 186 | + continue; |
| 187 | + } |
| 188 | + |
| 189 | + count = fread(buf, BUFSIZ - 1, sizeof(char), fifo); |
| 190 | + buf[count] = '\0'; |
| 191 | + |
| 192 | + fclose(fifo); |
| 193 | + |
| 194 | + if (strcmp(buf, "start") == 0) |
| 195 | + logins++; |
| 196 | + else if (strcmp(buf, "stop") == 0) |
| 197 | + logins--; |
| 198 | + else if (strcmp(buf, "shutdown") == 0) |
| 199 | + break; |
| 200 | + else |
| 201 | + elog(LOG_WARNING, "Ignoring unknown command %s.", buf); |
| 202 | + |
| 203 | + if (logins == 0) |
| 204 | + break; |
| 205 | + } |
| 206 | + |
| 207 | + do_openrc(false); |
| 208 | + |
| 209 | + return 0; |
| 210 | +} |
0 commit comments