From e48b3f4d5002deced980f5ef8e1c4fe3eb884fbc Mon Sep 17 00:00:00 2001 From: Nate Straz Date: Wed, 24 Aug 2005 21:50:03 +0000 Subject: Add a new packet type "setuser" so we can become any user/group combination. With this mod, `qarsh foo@host` should look the same as `ssh foo@host.` --- qarsh/qarsh.c | 56 ++++++++++++++++++++++++++++++++++++++-- qarsh/qarsh_packet.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++ qarsh/qarsh_packet.h | 12 ++++++++- qarsh/qarshd.c | 36 ++++++++++++++++++++++++++ 4 files changed, 173 insertions(+), 3 deletions(-) diff --git a/qarsh/qarsh.c b/qarsh/qarsh.c index 20e7023..0b91f5d 100644 --- a/qarsh/qarsh.c +++ b/qarsh/qarsh.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "sockutil.h" @@ -31,7 +32,7 @@ int qarsh_fd = -1; /* The control connection to qarshd */ void usage() { - printf("Usage: qarsh hostname cmdline ...\n"); + printf("qarsh: [user[.group]@]hostname cmdline ...\n"); } char * @@ -58,6 +59,25 @@ copyargs(char **argv) return args; } +void +set_remote_user(char *user, char *group) +{ + struct qa_packet *qp; + + qp = make_qp_setuser(user, group); + qp->qp_seq = 1; + send_packet(qarsh_fd, qp); + qpfree(qp); + qp = recv_packet(qarsh_fd); + if (qp && qp->qp_type == QP_RETURNCODE + && qp->qp_returncode.qp_rc == -1) { + + fprintf(stderr, "Remote side failed, %s\n", + qp->qp_returncode.qp_strerror); + close(qarsh_fd); + exit(125); + } +} int run_remote_cmd(char *cmdline) @@ -219,13 +239,22 @@ main(int argc, char *argv[]) int c; int port = 5008; char *host; + char *remuser = NULL; + char *remgroup = NULL; char *args; + struct passwd *pw; int ret; openlog("qarsh", LOG_PID, LOG_DAEMON); - while ((c = getopt(argc, argv, "+p:")) != -1) { + while ((c = getopt(argc, argv, "+p:l:g:")) != -1) { switch (c) { + case 'l': + remuser = strdup(optarg); + break; + case 'g': + remgroup = strdup(optarg); + break; case 'p': port = atoi(optarg); break; @@ -240,6 +269,27 @@ main(int argc, char *argv[]) usage(); exit(1); } + /* check for user and group in form [user[.group]@]hostname */ + { + char *sp; + + if ((sp = strchr(host, '@'))) { + remuser = host; + host = sp+1; + *sp = '\0'; + } + if (remuser && (sp = strchr(remuser, '.'))) { + remgroup = sp+1; + *sp = '\0'; + } + } + if (!(pw = getpwuid(getuid()))) { + fprintf(stderr, "qarsh: unknown user id.\n"); + exit(1); + } + if (remuser == NULL) { + remuser = strdup(pw->pw_name); + } argc -= optind; argv += optind; @@ -259,6 +309,8 @@ main(int argc, char *argv[]) return 127; } + set_remote_user(remuser, remgroup); + ret = run_remote_cmd(args); close(qarsh_fd); free(args); diff --git a/qarsh/qarsh_packet.c b/qarsh/qarsh_packet.c index 929e204..cc4f082 100644 --- a/qarsh/qarsh_packet.c +++ b/qarsh/qarsh_packet.c @@ -16,21 +16,25 @@ int parse_qp_returncode(xmlXPathContextPtr ctxt, struct qa_packet *qp); int parse_qp_ack(xmlXPathContextPtr ctxt, struct qa_packet *qp); int parse_qp_runcmd(xmlXPathContextPtr ctxt, struct qa_packet *qp); int parse_qp_cmdexit(xmlXPathContextPtr ctxt, struct qa_packet *qp); +int parse_qp_setuser(xmlXPathContextPtr ctxt, struct qa_packet *qp); void string_qp_hello(xmlNodePtr node, struct qa_packet *qp); void string_qp_returncode(xmlNodePtr node, struct qa_packet *qp); void string_qp_runcmd(xmlNodePtr node, struct qa_packet *qp); void string_qp_ack(xmlNodePtr node, struct qa_packet *qp); void string_qp_cmdexit(xmlNodePtr node, struct qa_packet *qp); +void string_qp_setuser(xmlNodePtr node, struct qa_packet *qp); void free_qp_hello(struct qa_packet *qp); void free_qp_returncode(struct qa_packet *qp); void free_qp_runcmd(struct qa_packet *qp); +void free_qp_setuser(struct qa_packet *qp); void dump_qp_ack(struct qa_packet *qp); void dump_qp_runcmd(struct qa_packet *qp); void dump_qp_returncode(struct qa_packet *qp); void dump_qp_cmdexit(struct qa_packet *qp); +void dump_qp_setuser(struct qa_packet *qp); struct packet_internals { @@ -76,6 +80,12 @@ struct packet_internals { .pi_string = string_qp_cmdexit, .pi_free = NULL, .pi_dump = dump_qp_cmdexit + }, { + .pi_name = "setuser", + .pi_parse = parse_qp_setuser, + .pi_string = string_qp_setuser, + .pi_free = free_qp_setuser, + .pi_dump = dump_qp_setuser } }; @@ -177,6 +187,14 @@ parse_qp_cmdexit(xmlXPathContextPtr ctxt, struct qa_packet *qp) free(s); return 0; } + +int +parse_qp_setuser(xmlXPathContextPtr ctxt, struct qa_packet *qp) +{ + qp->qp_setuser.qp_user = get_xpath_string(ctxt, "param[@name='user']"); + qp->qp_setuser.qp_group = get_xpath_string(ctxt, "param[@name='group']"); + return 0; +} struct qa_packet * parse_packet(xmlXPathContextPtr ctxt) @@ -323,6 +341,14 @@ void string_qp_cmdexit(xmlNodePtr node, struct qa_packet *qp) xmlAddChild(node, make_param("status", tmpstr)); } +void string_qp_setuser(xmlNodePtr node, struct qa_packet *qp) +{ + xmlAddChild(node, make_param("user", qp->qp_setuser.qp_user)); + if (qp->qp_setuser.qp_group) { + xmlAddChild(node, make_param("group", qp->qp_setuser.qp_group)); + } +} + /* Must pass in a pointer, but not a malloc'ed pointer */ char * qptostr(struct qa_packet *qp, char **qpstr, int *qpsize) @@ -371,6 +397,23 @@ make_qp_hello(char *greeting) return qp; } +struct qa_packet * +make_qp_returncode(int rc, int eno, char *strerr) +{ + struct qa_packet *qp; + + qp = malloc(sizeof *qp); + assert(qp); + memset(qp, 0, sizeof *qp); + + qp->qp_type = QP_RETURNCODE; + qp->qp_returncode.qp_rc = rc; + qp->qp_returncode.qp_errno = eno; + qp->qp_returncode.qp_strerror = strdup(strerr); + + return qp; +} + struct qa_packet * make_qp_ack(enum qa_packet_type t, int i) { @@ -418,6 +461,21 @@ make_qp_cmdexit(pid_t pid, int status) return qp; } +struct qa_packet * +make_qp_setuser(char *user, char *group) +{ + struct qa_packet *qp; + qp = malloc(sizeof *qp); + assert(qp); + memset(qp, 0, sizeof *qp); + + qp->qp_type = QP_SETUSER; + qp->qp_setuser.qp_user = strdup(user); + if (group) qp->qp_setuser.qp_group = strdup(group); + + return qp; +} + /* * Packet deallocation functions */ @@ -439,6 +497,13 @@ free_qp_runcmd(struct qa_packet *qp) condfree(qp->qp_runcmd.qp_cmdline); } +void +free_qp_setuser(struct qa_packet *qp) +{ + condfree(qp->qp_setuser.qp_user); + condfree(qp->qp_setuser.qp_group); +} + void qpfree(struct qa_packet *qp) { @@ -487,6 +552,13 @@ dump_qp_cmdexit(struct qa_packet *qp) } } +void +dump_qp_setuser(struct qa_packet *qp) +{ + printf("\tuser: %s\n", qp->qp_setuser.qp_user); + printf("\tgroup: %s\n", qp->qp_setuser.qp_group); +} + void dump_qp(struct qa_packet *qp) { diff --git a/qarsh/qarsh_packet.h b/qarsh/qarsh_packet.h index b21661b..b6d821b 100644 --- a/qarsh/qarsh_packet.h +++ b/qarsh/qarsh_packet.h @@ -11,7 +11,8 @@ enum qa_packet_type { QP_RETURNCODE = 2, QP_RUNCMD = 3, QP_ACK = 4, - QP_CMDEXIT = 5 + QP_CMDEXIT = 5, + QP_SETUSER = 6 }; struct qp_hello_pkt { @@ -42,6 +43,11 @@ struct qp_cmdexit_pkt { int qp_status; }; +struct qp_setuser_pkt { + char *qp_user; + char *qp_group; +}; + #define QP_VERSION 1 struct qa_packet { @@ -53,6 +59,7 @@ struct qa_packet { struct qp_runcmd_pkt runcmd; struct qp_ack_pkt ack; struct qp_cmdexit_pkt cmdexit; + struct qp_setuser_pkt setuser; } qp_u; }; @@ -61,14 +68,17 @@ struct qa_packet { #define qp_runcmd qp_u.runcmd #define qp_ack qp_u.ack #define qp_cmdexit qp_u.cmdexit +#define qp_setuser qp_u.setuser /* Prototypes */ char *qp_packet_type(enum qa_packet_type t); struct qa_packet *parse_packets(char *buf, int n); struct qa_packet *make_qp_hello(char *greeting); +struct qa_packet *make_qp_returncode(int rc, int eno, char *strerr); struct qa_packet *make_qp_ack(enum qa_packet_type t, int i); struct qa_packet *make_qp_runcmd(char *cmdline, int p_in, int p_out, int p_err); struct qa_packet *make_qp_cmdexit(pid_t pid, int status); +struct qa_packet *make_qp_setuser(char *user, char *group); char *qptostr(struct qa_packet *qp, char **qpstr, int *qpsize); void qpfree(struct qa_packet *qp); void dump_qp(struct qa_packet *qp); diff --git a/qarsh/qarshd.c b/qarsh/qarshd.c index 9a90782..d443704 100644 --- a/qarsh/qarshd.c +++ b/qarsh/qarshd.c @@ -11,6 +11,9 @@ #include #include #include +#include +#include + #include "sockutil.h" #include "qarsh_packet.h" @@ -23,6 +26,28 @@ /* Globals */ struct sockaddr_in peername; +int +setup_user(char *user, char *group) +{ + struct passwd *pw; + struct group *grp; + + if ((pw = getpwnam(user)) == NULL) { + syslog(LOG_WARNING, "User \"%s\" not found\n", user); + return 0; + } + + if (group && (grp = getgrnam(group))) { + setgid(grp->gr_gid); + initgroups(pw->pw_name, grp->gr_gid); + } else { + setgid(pw->pw_gid); + initgroups(pw->pw_name, pw->pw_gid); + } + /* setuid is last so we can still do setgid and initgroups */ + setuid(pw->pw_uid); + return 1; +} pid_t run_cmd(const char *cmd, int p_in, int p_out, int p_err) @@ -109,6 +134,16 @@ handle_packets(int infd) break; } switch (qp->qp_type) { + case QP_SETUSER: + if (setup_user(qp->qp_setuser.qp_user, + qp->qp_setuser.qp_group) == 0) { + rp = make_qp_returncode(-1, 0, "User not found"); + } else { + rp = make_qp_ack(QP_SETUSER, 1); + } + send_packet(fileno(stdout), rp); + qpfree(rp); + break; case QP_RUNCMD: child_pid = run_cmd(qp->qp_runcmd.qp_cmdline, qp->qp_runcmd.qp_stdin_port, @@ -117,6 +152,7 @@ handle_packets(int infd) waitpid(child_pid, &child_status, 0); rp = make_qp_cmdexit(child_pid, child_status); send_packet(fileno(stdout), rp); + qpfree(rp); break; default: syslog(LOG_WARNING, -- cgit