diff --git a/appl/rsh/rsh.c b/appl/rsh/rsh.c index 77bbf9ce4..4dad235b8 100644 --- a/appl/rsh/rsh.c +++ b/appl/rsh/rsh.c @@ -1,20 +1,12 @@ #include "rsh_locl.h" RCSID("$Id$"); -/* - * - */ - -enum auth_method { AUTH_KRB4, AUTH_KRB5 } auth_method; +static enum auth_method auth_method; /* * */ -/* - * Encryption with krb5 should probably use des-cbc-crc - */ - #define RSH_BUFSIZ 10240 static krb5_context context; @@ -80,7 +72,7 @@ do_write (int fd, void *buf, size_t sz) if(auth_method == AUTH_KRB4) { return des_enc_write (fd, buf, sz, schedule, &iv); } else if(auth_method == AUTH_KRB5) { - int status; + krb5_error_code status; krb5_data data; u_int32_t len; int ret; @@ -187,7 +179,7 @@ send_krb4_auth(int s, struct sockaddr_in thisaddr, s, &text, "rcmd", hostname, krb_realmofhost (hostname), getpid(), &msg, &cred, schedule, - &thisaddr, &thataddr, "KCMDV0.1"); + &thisaddr, &thataddr, KCMD_VERSION); if (status != KSUCCESS) errx ("%s: %s", hostname, krb_get_err_text(status)); memcpy (iv, cred.session, sizeof(iv)); @@ -197,7 +189,6 @@ send_krb4_auth(int s, struct sockaddr_in thisaddr, err (1, "write"); if (krb_net_write (s, cmd, cmd_len) != cmd_len) err (1, "write"); - free (cmd); } static void @@ -241,7 +232,7 @@ send_krb5_auth(int s, struct sockaddr_in thisaddr, status = krb5_sendauth (context, &auth_context, &s, - "KCMDV0.1", + KCMD_VERSION, NULL, server, do_encrypt ? AP_OPTS_MUTUAL_REQUIRED : 0, @@ -254,8 +245,6 @@ send_krb5_auth(int s, struct sockaddr_in thisaddr, if (status) errx ("%s: %s", hostname, krb5_get_err_text(context, status)); - free (buf); - keyblock = auth_context->key; len = strlen(local_user) + 1; @@ -342,6 +331,8 @@ proto (int s, char *hostname, char *local_user, char *remote_user, else abort (); + free (cmd); + if (krb_net_read (s, &reply, 1) != 1) err (1, "read"); if (reply != 0) { diff --git a/appl/rsh/rsh_locl.h b/appl/rsh/rsh_locl.h index dd8a522a1..d2a2f7954 100644 --- a/appl/rsh/rsh_locl.h +++ b/appl/rsh/rsh_locl.h @@ -5,6 +5,8 @@ #endif #include +#include +#include #include #include #include @@ -12,6 +14,19 @@ #include #include #include +#include #include +#include #include #include + +/* + * + */ + +enum auth_method { AUTH_KRB4, AUTH_KRB5 }; + +#define KCMD_VERSION "KCMDV0.1" + +#define USERNAME_SZ 16 +#define COMMAND_SZ 1024 diff --git a/appl/rsh/rshd.c b/appl/rsh/rshd.c new file mode 100644 index 000000000..53bf7b90b --- /dev/null +++ b/appl/rsh/rshd.c @@ -0,0 +1,252 @@ +#include "rsh_locl.h" +RCSID("$Id$"); + +static enum auth_method auth_method; + +static krb5_context context; +static krb5_auth_context auth_context; +static krb5_keyblock keyblock; +static des_key_schedule schedule; +static des_cblock iv; + +static int do_encrypt; + +static void +syslog_and_die (int prio, const char *m, ...) +{ + va_list args; + + va_start(args, m); + vsyslog (prio, m, args); + va_end(args); + exit (1); +} + +static void +fatal (int sock, const char *m, ...) +{ + va_list args; + char buf[BUFSIZ]; + size_t len; + + *buf = 1; + va_start(args, m); + len = vsnprintf (buf + 1, sizeof(buf) - 1, m, args); + va_end(args); + syslog (LOG_ERR, buf + 1); + krb_net_write (sock, buf, len + 1); + exit (1); +} + +static void +read_str (int s, char *str, size_t sz, char *expl) +{ + while (sz > 0) { + if (krb_net_read (s, str, 1) != 1) + syslog_and_die (LOG_ERR, "read: %m"); + if (*str == '\0') + return; + --sz; + ++str; + } + fatal (s, "%s too long", expl); +} + +static int +recv_krb4_auth (int s, u_char *buf, + struct sockaddr_in thisaddr, + struct sockaddr_in thataddr, + char *client_username, + char *server_username, + char *cmd) +{ + int status; + int32_t options; + KTEXT_ST ticket; + AUTH_DAT nauth; + char instance[INST_SZ + 1]; + char version[KRB_SENDAUTH_VLEN + 1]; + + if (memcmp (buf, KRB_SENDAUTH_VERS, 4) != 0) + return -1; + if (krb_net_read (s, buf + 4, KRB_SENDAUTH_VLEN - 4) != + KRB_SENDAUTH_VLEN - 4) + syslog_and_die (LOG_ERR, "reading auth info: %m"); + if (memcmp (buf, KRB_SENDAUTH_VERS, KRB_SENDAUTH_VLEN) != 0) + syslog_and_die(LOG_ERR, + "unrecognized auth protocol: %.8s", buf); + + options = KOPT_IGNORE_PROTOCOL; + if (do_encrypt) + options |= KOPT_DO_MUTUAL; + k_getsockinst (s, instance, sizeof(instance)); + status = krb_recvauth (options, + s, + &ticket, + "rcmd", + instance, + &thataddr, + &thisaddr, + &auth, + "", + schedule, + version); + if (status != KSUCCESS) + syslog_and_die (LOG_ERR, "recvauth: %s", krb_get_err_text(status)); + if (strncmp (version, KCMD_VERSION, KRB_SENDAUTH_VLEN) != 0) + syslog_and_die (LOG_ERR, "bad version: %s", version); + + read_str (s, server_username, USERNAME_SZ, "remote username"); + if (kuserok (&auth, server_username) != 0) + fatal (s, "Permission denied"); + read_str (s, cmd, COMMAND_SZ, "command"); + return 0; +} + +static int +recv_krb5_auth (int s, u_char *buf, + struct sockaddr_in thisaddr, + struct sockaddr_in thataddr, + char *client_username, + char *server_username, + char *cmd) +{ + u_int32_t len; + krb5_auth_context *auth_context = NULL; + krb5_ticket *ticket; + krb5_error_code status; + + if (memcmp (buf, "\x00\x00\x00\x13", 4) != 0) + return -1; + len = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3]); + + if (krb_net_read(s, buf, len) != len) + syslog_and_die (LOG_ERR, "reading auth info: %m"); + if (len != sizeof(KRB5_SENDAUTH_VERSION) + || memcmp (buf, KRB5_SENDAUTH_VERSION, len) != 0) + syslog_and_die (LOG_ERR, "bad sendauth version: %.8s", buf); + + krb5_init_context (&context); + + status = krb5_recvauth(context, + &auth_context, + &s, + KCMD_VERSION, + server, + KRB5_RECVAUTH_IGNORE_VERSION, + NULL, + &ticket); + if (status) + syslog_and_die (LOG_ERR, "krb5_recvauth: %s", + krb5_get_err_text(context, status)); + keyblock = auth_context->key; + + read_str (s, client_username, USERNAME_SZ, "local username"); + read_str (s, cmd, COMMAND_SZ, "command"); + read_str (s, server_username, USERNAME_SZ, "remote username"); + + /* discard forwarding information */ + krb_net_read (s, buf, 4); + + if(!krb5_kuserok (context, + ticket->enc_part2.client, + server_username)) + fatal (s, "Permission denied"); + + if (strncmp (cmd, "-x ", 3) == 0) { + do_encrypt = 1; + memmove (cmd + 3, cmd, strlen(cmd) - 3); + } + return 0; +} + +static int +doit (int s) +{ + u_char buf[BUFSIZ]; + char *p; + struct sockaddr_in thisaddr, thataddr, erraddr; + int addrlen; + int port; + int errsock = -1; + char client_user[16], server_user[16]; + char cmd[COMMAND_SZ]; + + addrlen = sizeof(thisaddr); + if (getsockname (s, (struct sockaddr *)&thisaddr, &addrlen) < 0 + || addrlen != sizeof(thisaddr)) { + err (1, "getsockname(%s)", hostname); + } + addrlen = sizeof(thataddr); + if (getpeername (s, (struct sockaddr *)&thataddr, &addrlen) < 0 + || addrlen != sizeof(thataddr)) { + err (1, "getpeername(%s)", hostname); + } + + p = buf; + port = 0; + for(;;) { + if (krb_net_read (s, p, 1) != 1) + syslog_and_die (LOG_ERR, "reading port number: %m"); + if (*p == '\0') + break; + else if (isdigit(*p)) + port = port * 10 + *p - '0'; + else + syslog_and_die (LOG_ERR, "non-digit in port number: %c", *p); + } + if (port) { + erraddr = thataddr; + errsock = socket (AF_INET, SOCK_STREAM, 0); + if (errsock < 0) + syslog_and_die (LOG_ERR, "socket: %m"); + if (connect (errsock, (struct sockaddr *)&erradr, sizeof(erraddr)) < 0) + syslog_and_die (LOG_ERR, "connect: %m"); + } + + if (krb_net_read (s, buf, 4) != 4) + syslog_and_die (LOG_ERR, "reading auth info: %m"); + + if (recv_krb4_auth (s, buf, thisaddr, thataddr, + client_user, + server_user, + cmd) == 0) + auth_method = AUTH_KRB4; + else if(recv_krb5_auth (s, buf, thisaddr, thataddr, + client_user, + server_user, + cmd) == 0) + auth_method = AUTH_KRB5; + else + syslog_and_die (LOG_ERR, + "unrecognized auth protocol: %x %x %x %x", + buf[0], buf[1], buf[2], buf[3]); + + + + +} + +int +main(int argc, char **argv) +{ + int c; + int inetd = 0; + + set_progname (argv[0]); + openlog ("rshd", LOG_ODELAY, LOG_AUTH); + + while ((c = getopt(argc, argv, "ix")) != EOF) { + switch (c) { + case 'i' : + inetd = 1; + break; + case 'x' : + do_encrypt = 1; + break; + default : + usage (); + } + } + return doit (STDIN_FILENO); +}