terom@0: #include terom@0: #include terom@0: #include terom@0: #include terom@0: #include terom@0: #include terom@0: #include terom@0: #include terom@0: #include terom@0: terom@0: #ifndef XMSH_PATH terom@0: #error must define XMSH_PATH terom@0: #endif terom@0: terom@0: #define STRINGIFY(x) XSTRINGIFY(x) terom@0: #define XSTRINGIFY(x) #x terom@0: terom@0: #define USERNAME_MAX 64 terom@0: #define VMNAME_MAX 64 terom@0: terom@0: #define USERFILE_PATH_FMT (STRINGIFY(XMSH_PATH) "/users/%s") terom@0: #define USERFILE_PATH_MAX 64 + USERNAME_MAX terom@0: terom@0: #define XM_PATH "/usr/sbin/xm" terom@0: terom@0: /* terom@0: * Parse command: terom@0: * xmsh terom@0: * xmsh -c terom@0: */ terom@0: char *parse_command (int argc, char **argv) { terom@0: if (argc == 2) terom@0: return argv[1]; terom@0: terom@0: else if (argc == 3 && strcmp(argv[1], "-c") == 0) terom@0: return argv[2]; terom@0: terom@0: // fail terom@0: errx(EXIT_FAILURE, "usage: ssh [-t] (list|reboot|console)"); terom@0: } terom@0: terom@0: /* terom@0: * Validate that the given command is legal terom@0: */ terom@0: void validate_command (const char *command) { terom@0: // strcmp against whitelist of commands terom@0: if (0 terom@0: || (strcmp(command, "list") == 0) terom@0: || (strcmp(command, "reboot") == 0) terom@0: || (strcmp(command, "console") == 0) terom@0: ) terom@0: return; terom@0: terom@0: // else fail terom@0: err(EXIT_FAILURE, "invalid command: %s", command); terom@0: } terom@0: terom@0: /* terom@0: * Validate that the username is sane terom@0: */ terom@0: void validate_username (const char *c) { terom@0: if (!(*c)) terom@0: errx(EXIT_FAILURE, "username length"); terom@0: terom@0: for (; *c; c++) { terom@0: if (!isalpha(*c) && *c != '-') { terom@0: break; terom@0: } terom@0: } terom@0: terom@0: if (*c) terom@0: errx(EXIT_FAILURE, "username non-alpha"); terom@0: terom@0: } terom@0: terom@0: void validate_vmname (const char *c) { terom@0: if (!(*c)) terom@0: errx(EXIT_FAILURE, "vmname length"); terom@0: terom@0: for (; *c; c++) { terom@0: if (!isprint(*c) || isspace(*c)) { terom@0: break; terom@0: } terom@0: } terom@0: terom@0: if (*c) terom@0: errx(EXIT_FAILURE, "vmname non-print/space"); terom@0: } terom@0: terom@0: /* terom@0: * Get the real uid's username - i.e. the user who executed this file. terom@0: */ terom@0: void get_username (char buf[USERNAME_MAX]) { terom@0: uid_t uid; terom@0: struct passwd *passwd; terom@0: terom@0: // get the real uid terom@0: uid = getuid(); terom@0: terom@0: // get the passwd entry terom@0: if ((passwd = getpwuid(uid)) == NULL) terom@0: err(EXIT_FAILURE, "getpwuid"); terom@0: terom@0: if (passwd->pw_name == NULL) terom@0: errx(EXIT_FAILURE, "passwd->pw_name"); terom@0: terom@0: // fail too-long usernames terom@0: if (strlen(passwd->pw_name) >= USERNAME_MAX) terom@0: errx(EXIT_FAILURE, "strlen(passwd->pw_name) >= USERNAME_MAX"); terom@0: terom@0: // copy the username to buf terom@0: strncpy(buf, passwd->pw_name, USERNAME_MAX); terom@0: terom@0: // force zero-terminate terom@0: buf[USERNAME_MAX - 1] = '\0'; terom@0: terom@0: // sanity-check username to be all-alpha terom@0: validate_username(buf); terom@0: } terom@0: terom@0: /* terom@0: * Get the virtual machine name for the current user terom@0: */ terom@0: void get_vmname (const char *username, char buf[VMNAME_MAX]) { terom@0: // the path to the userfile terom@0: char path[USERFILE_PATH_MAX], *nl; terom@0: FILE *fh; terom@0: terom@0: // format the userfile path terom@0: if (snprintf(path, USERFILE_PATH_MAX, USERFILE_PATH_FMT, username) >= USERFILE_PATH_MAX) terom@0: errx(EXIT_FAILURE, "USERFILE_PATH_MAX"); terom@0: terom@0: // open the userfile terom@0: if ((fh = fopen(path, "r")) == NULL) { terom@0: if (errno == ENOENT) terom@0: errx(EXIT_FAILURE, "no vm defined for user: %s", username); terom@0: else terom@0: err(EXIT_FAILURE, "fopen: %s", path); terom@0: } terom@0: terom@0: // read the vmname terom@0: if (fgets(buf, VMNAME_MAX, fh) == NULL) terom@0: err(EXIT_FAILURE, "fgets: %s", path); terom@0: terom@0: // kill the newline terom@0: if ((nl = index(buf, '\n'))) terom@0: *nl = '\0'; terom@0: terom@0: // sanity-check the vmname terom@0: validate_vmname(buf); terom@0: terom@0: // good terom@0: } terom@0: terom@0: void __attribute__ ((noreturn)) xm_exec (const char *vmname, const char *command) { terom@0: const char *env[] = { NULL }; terom@0: terom@0: // setuid to root terom@0: if (setuid(0)) terom@0: err(EXIT_FAILURE, "setuid: 0"); terom@0: terom@0: // exec terom@0: execle(XM_PATH, "xm", command, vmname, NULL, env); terom@0: terom@0: // if we're still here, an error has occured terom@0: err(EXIT_FAILURE, "%s: %s %s", XM_PATH, command, vmname); terom@0: } terom@0: terom@0: int main (int argc, char **argv) { terom@0: char username[USERNAME_MAX], vmname[VMNAME_MAX], *command; terom@0: terom@0: // get command terom@0: command = parse_command(argc, argv); terom@0: terom@0: // get username terom@0: get_username(username); terom@0: terom@0: // get vmname terom@0: get_vmname(username, vmname); terom@0: terom@0: // execute xm terom@0: xm_exec(vmname, command); terom@0: } terom@0: