xmsh.c
changeset 0 e88b62deaec4
equal deleted inserted replaced
-1:000000000000 0:e88b62deaec4
       
     1 #include <stdlib.h>
       
     2 #include <unistd.h>
       
     3 #include <sys/types.h>
       
     4 #include <stdio.h>
       
     5 #include <string.h>
       
     6 #include <err.h>
       
     7 #include <ctype.h>
       
     8 #include <pwd.h>
       
     9 #include <errno.h>
       
    10 
       
    11 #ifndef XMSH_PATH
       
    12 #error must define XMSH_PATH
       
    13 #endif
       
    14 
       
    15 #define STRINGIFY(x) XSTRINGIFY(x)
       
    16 #define XSTRINGIFY(x) #x
       
    17 
       
    18 #define USERNAME_MAX 64
       
    19 #define VMNAME_MAX 64
       
    20 
       
    21 #define USERFILE_PATH_FMT (STRINGIFY(XMSH_PATH) "/users/%s")
       
    22 #define USERFILE_PATH_MAX 64 + USERNAME_MAX
       
    23 
       
    24 #define XM_PATH "/usr/sbin/xm"
       
    25 
       
    26 /*
       
    27  * Parse command:
       
    28  *  xmsh <cmd>
       
    29  *  xmsh -c <cmd>
       
    30  */
       
    31 char *parse_command (int argc, char **argv) {
       
    32     if (argc == 2)
       
    33         return argv[1];
       
    34 
       
    35     else if (argc == 3 && strcmp(argv[1], "-c") == 0)
       
    36         return argv[2];
       
    37     
       
    38     // fail
       
    39     errx(EXIT_FAILURE, "usage: ssh [-t] <dom0> (list|reboot|console)");
       
    40 }
       
    41 
       
    42 /*
       
    43  * Validate that the given command is legal
       
    44  */
       
    45 void validate_command (const char *command) {
       
    46     // strcmp against whitelist of commands
       
    47     if (0
       
    48         ||  (strcmp(command, "list") == 0)
       
    49         ||  (strcmp(command, "reboot") == 0)
       
    50         ||  (strcmp(command, "console") == 0)
       
    51     )
       
    52         return;
       
    53     
       
    54     // else fail
       
    55     err(EXIT_FAILURE, "invalid command: %s", command);
       
    56 }
       
    57 
       
    58 /*
       
    59  * Validate that the username is sane
       
    60  */
       
    61 void validate_username (const char *c) {
       
    62     if (!(*c))
       
    63         errx(EXIT_FAILURE, "username length");
       
    64 
       
    65     for (; *c; c++) {
       
    66         if (!isalpha(*c) && *c != '-') {
       
    67             break;
       
    68         }
       
    69     }
       
    70 
       
    71     if (*c)
       
    72         errx(EXIT_FAILURE, "username non-alpha");
       
    73 
       
    74 }   
       
    75 
       
    76 void validate_vmname (const char *c) {
       
    77     if (!(*c))
       
    78         errx(EXIT_FAILURE, "vmname length");
       
    79 
       
    80     for (; *c; c++) {
       
    81         if (!isprint(*c) || isspace(*c)) {
       
    82             break;
       
    83         }
       
    84     }
       
    85 
       
    86     if (*c)
       
    87         errx(EXIT_FAILURE, "vmname non-print/space");
       
    88 }
       
    89 
       
    90 /*
       
    91  * Get the real uid's username - i.e. the user who executed this file.
       
    92  */
       
    93 void get_username (char buf[USERNAME_MAX]) {
       
    94     uid_t uid;
       
    95     struct passwd *passwd;
       
    96 
       
    97     // get the real uid
       
    98     uid = getuid();
       
    99     
       
   100     // get the passwd entry
       
   101     if ((passwd = getpwuid(uid)) == NULL)
       
   102         err(EXIT_FAILURE, "getpwuid");
       
   103 
       
   104     if (passwd->pw_name == NULL)
       
   105         errx(EXIT_FAILURE, "passwd->pw_name");
       
   106 
       
   107     // fail too-long usernames
       
   108     if (strlen(passwd->pw_name) >= USERNAME_MAX)
       
   109         errx(EXIT_FAILURE, "strlen(passwd->pw_name) >= USERNAME_MAX");
       
   110     
       
   111     // copy the username to buf
       
   112     strncpy(buf, passwd->pw_name, USERNAME_MAX);
       
   113 
       
   114     // force zero-terminate
       
   115     buf[USERNAME_MAX - 1] = '\0';
       
   116     
       
   117     // sanity-check username to be all-alpha
       
   118     validate_username(buf);
       
   119 }
       
   120 
       
   121 /*
       
   122  * Get the virtual machine name for the current user
       
   123  */
       
   124 void get_vmname (const char *username, char buf[VMNAME_MAX]) {
       
   125     // the path to the userfile
       
   126     char path[USERFILE_PATH_MAX], *nl;
       
   127     FILE *fh;
       
   128     
       
   129     // format the userfile path
       
   130     if (snprintf(path, USERFILE_PATH_MAX, USERFILE_PATH_FMT, username) >= USERFILE_PATH_MAX)
       
   131         errx(EXIT_FAILURE, "USERFILE_PATH_MAX");
       
   132     
       
   133     // open the userfile
       
   134     if ((fh = fopen(path, "r")) == NULL) {
       
   135         if (errno == ENOENT)
       
   136             errx(EXIT_FAILURE, "no vm defined for user: %s", username);
       
   137         else
       
   138             err(EXIT_FAILURE, "fopen: %s", path);
       
   139     }
       
   140 
       
   141     // read the vmname
       
   142     if (fgets(buf, VMNAME_MAX, fh) == NULL)
       
   143         err(EXIT_FAILURE, "fgets: %s", path);
       
   144     
       
   145     // kill the newline
       
   146     if ((nl = index(buf, '\n')))
       
   147         *nl = '\0';
       
   148     
       
   149     // sanity-check the vmname
       
   150     validate_vmname(buf);
       
   151 
       
   152     // good
       
   153 }
       
   154 
       
   155 void __attribute__ ((noreturn)) xm_exec (const char *vmname, const char *command) {
       
   156     const char *env[] = { NULL };
       
   157     
       
   158     // setuid to root
       
   159     if (setuid(0))
       
   160         err(EXIT_FAILURE, "setuid: 0");
       
   161 
       
   162     // exec
       
   163     execle(XM_PATH, "xm", command, vmname, NULL, env);
       
   164 
       
   165     // if we're still here, an error has occured
       
   166     err(EXIT_FAILURE, "%s: %s %s", XM_PATH, command, vmname);
       
   167 }
       
   168 
       
   169 int main (int argc, char **argv) {
       
   170     char username[USERNAME_MAX], vmname[VMNAME_MAX], *command;
       
   171 
       
   172     // get command
       
   173     command = parse_command(argc, argv);
       
   174 
       
   175     // get username
       
   176     get_username(username);
       
   177 
       
   178     // get vmname
       
   179     get_vmname(username, vmname);
       
   180 
       
   181     // execute xm
       
   182     xm_exec(vmname, command);
       
   183 }
       
   184