From: Theodore Tso Date: Wed, 2 Jun 1993 13:31:03 +0000 (+0000) Subject: Checked in changes from ISI X-Git-Tag: krb5-1.0-beta3~304 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=cb5ae5617dd0c8176393a5adad22eb145ff523f7;p=krb5.git Checked in changes from ISI git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@2534 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/appl/bsd/krlogind.c b/src/appl/bsd/krlogind.c index 9360b6fef..9fb3e0c48 100644 --- a/src/appl/bsd/krlogind.c +++ b/src/appl/bsd/krlogind.c @@ -43,66 +43,80 @@ static char sccsid[] = "@(#)rlogind.c 5.17 (Berkeley) 8/31/88"; * data */ - /* - * This is the rlogin daemon. The very basic protocol for checking - * authentication and authorization is: - * 1) Check authentication. - * 2) Check authorization via the access-control files: - * ~/.k5login (using krb5_kuserok) and/or - * ~/.rhosts (using ruserok). - * 3) Prompt for password if any checks fail, or if so configured. - * Allow login if all goes well either by calling the accompanying login.krb - * or /bin/login, according to the definition of DO_NOT_USE_K_LOGIN. - * - * The configuration is done either by command-line arguments passed by inetd, - * or by the name of the daemon. If command-line arguments are present, they - * take priority. The options are: - * -k and -K means check .k5login (using krb5_kuserok). - * -r and -R means check .rhosts (using ruserok). - * -p and -P means prompt for password. - * The difference between upper and lower case is as follows: - * If lower case -r or -k, then as long as one of krb5_kuserok or ruserok - * passes, allow login without password. If the -p option is passed with -r - * or -k, then if both checks fail, allow login but only after password - * verification. - * If uppercase -R or -K, then those checks must be passed, regardless of - * other checks, else no login with or without password. - * If the -P option is passed, then the password is verified in - * addition to all other checks. If -p is not passed with -k or -r, and both - * checks fail, then login permission is denied. - * -x and -e means use encryption. - * If no command-line arguments are present, then the presence of the - * letters kKrRexpP in the program-name before "logind" determine the - * behaviour of the program exactly as with the command-line arguments. - * - * If the ruserok check is to be used, then the client should connect from a - * privileged port, else deny permission. - */ - - /* DEFINES: - * KERBEROS - Define this if application is to be kerberised. - * CRYPT - Define this if encryption is to be an option. - * DO_NOT_USE_K_LOGIN - Define this if you want to use /bin/login instead - * of the accompanying login.krb. In that case, the remote user's - * name must be present in the local .rhosts file, regardless of - * any options specified. - * LOG_ALL_LOGINS - Define this if you want to log all logins. - * LOG_OTHER_USERS - Define this if you want to log all principals that do - * not map onto the local user. - * LOG_REMOTE_REALM - Define this if you want to log all principals from - * remote realms. - * Note: Root logins are always logged. - */ +/* + * This is the rlogin daemon. The very basic protocol for checking + * authentication and authorization is: + * 1) Check authentication. + * 2) Check authorization via the access-control files: + * ~/.k5login (using krb5_kuserok) and/or + * ~/.rhosts (using ruserok). + * 3) Prompt for password if any checks fail, or if so configured. + * Allow login if all goes well either by calling the accompanying + * login.krb5 or /bin/login, according to the definition of + * DO_NOT_USE_K_LOGIN. + * + * The configuration is done either by command-line arguments passed by + * inetd, or by the name of the daemon. If command-line arguments are + * present, they take priority. The options are: + * -k and -K means check .k5login (using krb5_kuserok). + * -r and -R means check .rhosts (using ruserok). + * -p and -P means prompt for password. + * The difference between upper and lower case is as follows: + * If lower case -r or -k, then as long as one of krb5_kuserok or + * ruserok passes, allow login without password. If the -p option is + * passed with -r or -k, then if both checks fail, allow login but + * only after password verification. + * If uppercase -R or -K, then those checks must be passed, + * regardless of other checks, else no login with or without password. + * If the -P option is passed, then the password is verified in + * addition to all other checks. If -p is not passed with -k or -r, + * and both checks fail, then login permission is denied. + * -x and -e means use encryption. + * + * If no command-line arguments are present, then the presence of the + * letters kKrRexpP in the program-name before "logind" determine the + * behaviour of the program exactly as with the command-line arguments. + * + * If the ruserok check is to be used, then the client should connect + * from a privileged port, else deny permission. + */ +/* DEFINES: + * KERBEROS - Define this if application is to be kerberised. + * CRYPT - Define this if encryption is to be an option. + * DO_NOT_USE_K_LOGIN - Define this if you want to use /bin/login + * instead of the accompanying login.krb5. In that case, + * the remote user's name must be present in the local + * .rhosts file, regardless of any options specified. + * SERVE_V4 - Define this if v4 rlogin clients are also to be served. + * ALWAYS_V5_KUSEROK - Define this if you want .k5login to be + * checked even for v4 clients (instead of .klogin). + * SERVE_NON_KRB - Define this if non-kerberized rlogin clients are + * to be served. NOTE HOWEVER THAT THIS IS A SERIOUS + * SECURITY FLAW! + * LOG_ALL_LOGINS - Define this if you want to log all logins. + * LOG_OTHER_USERS - Define this if you want to log all principals + * that do not map onto the local user. + * LOG_REMOTE_REALM - Define this if you want to log all principals from + * remote realms. + * Note: Root logins are always logged. + */ + +/* This is usually done in site.def. If not, then uncomment this. +#define KERBEROS +*/ #define LOG_REMOTE_REALM -#define KERBEROS - +#define CRYPT +#define SERVE_V4 + #include #include #include #include #include #include +#include + /* #include ??? What system has a sys/unistd.h? */ #include @@ -152,20 +166,36 @@ struct winsize { #ifdef KERBEROS #include +#include #include #include #include #include -#include - #ifdef BUFSIZ #undef BUFSIZ #endif -#define BUFSIZ 4096 + +int V4 = 0; /* set when v4 client detected. */ +int non_privileged = 0; /* set when connection is seen to be from */ + /* a non-privileged port */ +#ifdef SERVE_V4 +#include +AUTH_DAT *v4_kdata; +KTEXT v4_ticket; +Key_schedule schedule; +int v4_des_read(), v4_des_write(); +#endif + +#define BUFSIZ 5120 + +int v5_des_read(), v5_des_write(); + +#include #define SECURE_MESSAGE "This rlogin session is using DES encryption for all data transmissions.\r\n" +int (*des_read)(), (*des_write)(); char des_inbuf[2*BUFSIZ]; /* needs to be > largest read size */ char des_outbuf[2*BUFSIZ]; /* needs to be > largest write size */ krb5_data desinbuf,desoutbuf; @@ -180,13 +210,14 @@ krb5_ticket *ticket = 0; #endif #endif -#define ARGSTR "rRkKeExXpP?" +#define ARGSTR "rRkKeExXpPD:?" #else /* !KERBEROS */ -#define ARGSTR "rRpP?" -#define des_read read -#define des_write write +#define ARGSTR "rRpPD:?" +#define (*des_read) read +#define (*des_write) write #endif /* KERBEROS */ +#ifndef LOGIN_PROGRAM #ifdef DO_NOT_USE_K_LOGIN #ifdef sysvimp #define LOGIN_PROGRAM "/bin/remlogin" @@ -194,20 +225,21 @@ krb5_ticket *ticket = 0; #define LOGIN_PROGRAM "/bin/login" #endif #else /* DO_NOT_USE_K_LOGIN */ -#define LOGIN_PROGRAM "/krb5/etc/login.krb5" -#endif +#define LOGIN_PROGRAM KRB5_PATH_LOGIN +#endif /* DO_NOT_USE_K_LOGIN */ +#endif /* LOGIN_PROGRAM */ struct utmp wtmp; -#define NMAX sizeof(wtmp.ut_name) #define MAXRETRIES 4 -#define UT_HOSTSIZE sizeof(((struct utmp *)0)->ut_host) +#define UT_NAMESIZE sizeof(((struct utmp *)0)->ut_name) #define MAX_PROG_NAME 16 -char lusername[NMAX+1]; -char *rusername = 0; +char lusername[UT_NAMESIZE+1]; +char rusername[UT_NAMESIZE+1]; char *krusername = 0; char term[64]; char rhost_name[128]; +krb5_principal client; extern int errno; int reapchild(); @@ -217,7 +249,7 @@ char *malloc(); #endif char *progname; -void fatal(), fatalperror(), doit(), usage(); +void fatal(), fatalperror(), doit(), usage(), do_krb_login(); int princ_maps_to_lname(), default_realm(); int must_pass_rhosts = 0, must_pass_k5 = 0, must_pass_one = 0; @@ -228,10 +260,12 @@ main(argc, argv) int argc; char **argv; { - extern int opterr, optind; + extern int opterr, optind, optarg; int on = 1, fromlen, ch, i; struct sockaddr_in from; char *options; + int debug_port = 0; + int fd; progname = *argv; @@ -267,11 +301,20 @@ main(argc, argv) options[0] = '\0'; for (i = 0; (progname[i] != '\0') && (i < MAX_PROG_NAME); i++) if (!strcmp(progname+i, "logind")) { + char **newargv; + + newargv = (char **) malloc(sizeof(char *) 3); + strcpy(options, "-"); strncat(options, progname, i); + argc = 2; - argv[1] = options; - argv[2] = NULL; + + newargv[0] = argv[0]; + newargv[1] = options; + newargv[2] = NULL; + + argv = newargv; break; } if (options[0] == '\0') { @@ -316,6 +359,9 @@ main(argc, argv) case 'P': /* passwd is a must */ passwd_req = 1; break; + case 'D': + debug_port = atoi(optarg); + break; case '?': default: usage(); @@ -326,19 +372,54 @@ main(argc, argv) argv += optind; fromlen = sizeof (from); - if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { - syslog(LOG_ERR,"Can't get peer name of remote host: %m"); + + if (debug_port) { + int s; + struct sockaddr_in sin; + + if ((s = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) < 0) { + fprintf(stderr, "Error in socket: %s\n", strerror(errno)); + exit(2); + } + + bzero((char *) &sin,sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(debug_port); + sin.sin_addr.s_addr = INADDR_ANY; + + if ((bind(s, (struct sockaddr *) &sin, sizeof(sin))) < 0) { + fprintf(stderr, "Error in bind: %s\n", strerror(errno)); + exit(2); + } + + if ((listen(s, 5)) < 0) { + fprintf(stderr, "Error in listen: %s\n", strerror(errno)); + exit(2); + } + + if ((fd = accept(s, &from, &fromlen)) < 0) { + fprintf(stderr, "Error in accept: %s\n", strerror(errno)); + exit(2); + } + + close(s); + } + else { + if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { + syslog(LOG_ERR,"Can't get peer name of remote host: %m"); #ifdef STDERR_FILENO - fatal(STDERR_FILENO, "Can't get peer name of remote host", 1); + fatal(STDERR_FILENO, "Can't get peer name of remote host", 1); #else - fatal(3, "Can't get peer name of remote host", 1); + fatal(3, "Can't get peer name of remote host", 1); #endif - - } - if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) + } + fd = 0; + } + + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); - doit(0, &from); + doit(fd, &from); } @@ -350,7 +431,6 @@ main(argc, argv) int child; int cleanup(); int netf; -krb5_principal client; char line[MAXPATHLEN]; extern char *inet_ntoa(); @@ -406,24 +486,18 @@ void doit(f, fromp) if ((must_pass_rhosts || must_pass_one) && (fromp->sin_port >= IPPORT_RESERVED || fromp->sin_port < IPPORT_RESERVED/2)) + non_privileged = 1; #else /* !KERBEROS */ - if (fromp->sin_port >= IPPORT_RESERVED || - fromp->sin_port < IPPORT_RESERVED/2) + if (fromp->sin_port >= IPPORT_RESERVED || + fromp->sin_port < IPPORT_RESERVED/2) + fatal(f, "Permission denied - Connection from bad port"); #endif /* KERBEROS */ - fatal(f, "Permission denied - Connection from bad port"); /* Set global netf to f now : we may need to drop everything in do_krb_login. */ netf = f; #if defined(KERBEROS) - /* - * If encrypting, we need to respond here, since we have to send - * the mutual authentication stuff before the response - * - * Do_krb_login has been modified to handle rlogin type requests - * also.... - */ /* All validation, and authorization goes through do_krb_login() */ do_krb_login(rhost_name); @@ -434,10 +508,9 @@ void doit(f, fromp) fatal(netf, "Permission denied"); } #else - rusername = malloc(sizeof (lusername) + 1); - getstr(rusername, sizeof(lusername), "remuser"); - getstr(lusername, sizeof(lusername), "locuser"); - getstr(term, sizeof(term), "Terminal type"); + getstr(f, rusername, sizeof(rusername), "remuser"); + getstr(f, lusername, sizeof(lusername), "locuser"); + getstr(f, term, sizeof(term), "Terminal type"); #endif write(f, "", 1); @@ -458,17 +531,23 @@ void doit(f, fromp) #ifdef NOFCHMOD if (chmod(t,0)) #else - if (fchmod(t, 0)) + if (fchmod(t, 0)) #endif - fatalperror(f, line); + fatalperror(f, line); #ifndef SYSV - signal(SIGHUP, SIG_IGN); - vhangup(); - signal(SIGHUP, SIG_DFL); + if (f == 0) { /* if operating standalone, do not reset tty!! */ + signal(SIGHUP, SIG_IGN); + vhangup(); + signal(SIGHUP, SIG_DFL); + } #ifdef ultrix /* Someone needs to cleanup all this and have a consistant way of associating controlling tty to a process. */ setpgrp(); #endif +#if defined (sun) || defined (POSIX) + setsid(); +#endif + t = open(line, O_RDWR); if (t < 0) fatalperror(f, line); @@ -570,46 +649,48 @@ void doit(f, fromp) if (pwd && (pwd->pw_uid == 0)) { if (passwd_req) syslog(LOG_NOTICE, "ROOT login by %s (%s@%s) forcing password access", - krusername, rusername, rhost_name); + krusername ? krusername : "", rusername, rhost_name); else syslog(LOG_NOTICE, "ROOT login by %s (%s@%s) ", - krusername, rusername, rhost_name); + krusername ? krusername : "", rusername, rhost_name); } #if defined(KERBEROS) && defined(LOG_REMOTE_REALM) && !defined(LOG_OTHER_USERS) && !defined(LOG_ALL_LOGINS) /* Log if principal is from a remote realm */ -else if (!default_realm(client)) + else if (client && !default_realm(client)) #endif #if defined(KERBEROS) && defined(LOG_OTHER_USERS) && !defined(LOG_ALL_LOGINS) - /* Log if principal name does not map to local username */ -else if (!princ_maps_to_lname(client, lusername)) + /* Log if principal name does not map to local username */ + else if (client && !princ_maps_to_lname(client, lusername)) #endif /* LOG_OTHER_USERS */ #ifdef LOG_ALL_LOGINS /* Log everything */ -else + else #endif #if defined(LOG_REMOTE_REALM) || defined(LOG_OTHER_USERS) || defined(LOG_ALL_LOGINS) - { - if (passwd_req) - syslog(LOG_NOTICE, - "login by %s (%s@%s) as %s forcing password access\n", - krusername, rusername, rhost_name, lusername); - else - syslog(LOG_NOTICE, - "login by %s (%s@%s) as %s\n", - krusername, rusername, rhost_name, lusername); - } + { + if (passwd_req) + syslog(LOG_NOTICE, + "login by %s (%s@%s) as %s forcing password access\n", + krusername ? krusername : "", rusername, + rhost_name, lusername); + else + syslog(LOG_NOTICE, + "login by %s (%s@%s) as %s\n", + krusername ? krusername : "", rusername, + rhost_name, lusername); + } #endif #ifdef DO_NOT_USE_K_LOGIN execl(LOGIN_PROGRAM, "login", "-r", rhost_name, 0); #else if (passwd_req) - execl(LOGIN_PROGRAM, "login", rhost_name,0); + execl(LOGIN_PROGRAM, "login","-r", rhost_name, 0); else - execl(LOGIN_PROGRAM, "login", "-f", rhost_name, 0); + execl(LOGIN_PROGRAM, "login", "-fr", rhost_name, 0); #endif fatalperror(2, LOGIN_PROGRAM, errno); @@ -630,21 +711,20 @@ else } #if defined(KERBEROS) - if (do_encrypt) - if ((des_write(f, SECURE_MESSAGE, sizeof(SECURE_MESSAGE))) < 0){ - sprintf(buferror, "Cannot encrypt-write network."); - fatal(p,buferror); - } - else - /* - * if encrypting, don't turn on NBIO, else the read/write routines - * will fail to work properly - */ -#endif /* KERBEROS */ - { - ioctl(f, FIONBIO, &on); - ioctl(p, FIONBIO, &on); + if (do_encrypt) { + if (((*des_write)(f, SECURE_MESSAGE, sizeof(SECURE_MESSAGE))) < 0){ + sprintf(buferror, "Cannot encrypt-write network."); + fatal(p,buferror); } + } + else + /* + * if encrypting, don't turn on NBIO, else the read/write routines + * will fail to work properly + */ +#endif /* KERBEROS */ + ioctl(f, FIONBIO, &on); + ioctl(p, FIONBIO, &on); #ifdef hpux /******** FIONBIO doesn't currently work on ptys, should be O_NDELAY? **/ /*** get flags and add O_NDELAY **/ @@ -659,11 +739,7 @@ else setpgrp(0, 0); #endif -#if defined(KERBEROS) - /* Pass down rusername and lusername which we have - obtained from ticket and authorized by PWC_ACCESS. - Note lusername's .rhost should have entry for rusername. - */ + /* Pass down rusername and lusername to login. */ (void) write(p, rusername, strlen(rusername) +1); (void) write(p, lusername, strlen(lusername) +1); /* stuff term info down to login */ @@ -674,7 +750,6 @@ else sprintf(buferror,"Cannot write slave pty %s ",line); fatalperror(f,buferror,errno); } -#endif /* KERBEROS */ protocol(f, p); signal(SIGCHLD, SIG_IGN); @@ -782,7 +857,7 @@ protocol(f, p) } } if (ibits & (1< 0) { - cc = des_write(f, pbp, pcc); + cc = (*des_write)(f, pbp, pcc); if (cc < 0 && errno == EWOULDBLOCK) { /* also shouldn't happen */ sleep(5); @@ -897,7 +972,7 @@ void fatal(f, msg) buf[0] = '\01'; /* error indicator */ (void) sprintf(buf + 1, "%s: %s.\r\n",progname, msg); if ((f == netf) && (pid > 0)) - (void) des_write(f, buf, strlen(buf)); + (void) (*des_write)(f, buf, strlen(buf)); else (void) write(f, buf, strlen(buf)); syslog(LOG_ERR,"%s\n",msg); @@ -933,152 +1008,84 @@ void fatalperror(f, msg) #ifdef KERBEROS - +void do_krb_login(host) char *host; { - int rc; krb5_error_code status; - struct sockaddr_in peersin; - krb5_address peeraddr; struct passwd *pwd; - krb5_principal server; - char srv_name[100]; - char def_host[100]; - krb5_data inbuf; - + if (getuid()) { exit(1); } - - /* we want mutual authentication */ -#ifdef unicos61 -#define SIZEOF_INADDR SIZEOF_in_addr -#else -#define SIZEOF_INADDR sizeof(struct in_addr) -#endif - - rc = sizeof(peersin); - if (getpeername(netf, (struct sockaddr *)&peersin, &rc)) { - syslog(LOG_ERR, "get peer name failed %d", netf); - exit(1); - } - - peeraddr.addrtype = peersin.sin_family; - peeraddr.length = SIZEOF_INADDR; - peeraddr.contents = (krb5_octet *)&peersin.sin_addr; - - strcpy(srv_name, "host/"); - gethostname(def_host, 100); - strcat(srv_name, def_host); - if (status = krb5_parse_name(srv_name, &server)) { - syslog(LOG_ERR, "parse server name %s: %s", "host", - error_message(status)); - exit(1); - } - krb5_princ_type(server) = KRB5_NT_SRV_HST; - - if (status = krb5_recvauth(&netf, - "KCMDV0.1", - server, /* no match on server - incase we have are - serving multiple realms*/ - &peeraddr, /* We do want to match this - against caddrs in the - ticket. */ - 0, /* use srv5tab */ - 0, /* no keyproc */ - 0, /* no keyproc arg */ - 0, /* no rc_type */ - 0, /* no seq number */ - &client, /* return client */ - &ticket, /* return ticket */ - &kdata /* return authenticator */ - )) { - syslog(LOG_ERR, - "Kerberos authentication failed from %s: %s\n", - host,error_message(status)); - - /* Dont exit out for klogin, but - grab locuser, terminal, and remuser. - */ - - /* These two reads will be used in the next release to obtain - a forwarded TGT and related info. */ - if (status = krb5_read_message((krb5_pointer)&netf, &inbuf)) - fatal(netf, "Error reading message"); - if (inbuf.length) - fatal(netf, "Forwarding is not yet supported"); - if (status = krb5_read_message((krb5_pointer)&netf, &inbuf)) - fatal(netf, "Error reading message"); - if (inbuf.length) - fatal(netf, "Forwarding is not yet supported"); - - getstr(lusername, sizeof(lusername), "locuser"); - getstr(term, sizeof(term), "Terminal type"); - rusername = malloc(sizeof (lusername) + 1); - getstr(rusername, sizeof(lusername), "remuser"); - + + /* Check authentication. This can be either Kerberos V5, */ + /* Kerberos V4, or host-based. */ + if (status = recvauth()) { failed_auth = 1; if (ticket) krb5_free_ticket(ticket); + if (status != 255) + syslog(LOG_ERR, + "Authentication failed from %s: %s\n", + host,error_message(status)); return; } - /* Setup up eblock if encrypted login session */ - /* otherwise zero out session key */ - if (do_encrypt) { - krb5_use_keytype(&eblock, - ticket->enc_part2->session->keytype); - if (status = krb5_process_key(&eblock, - ticket->enc_part2->session)) - fatal(netf, "Permission denied"); - } - - /* These two reads will be used in the next release to obtain - a forwarded TGT and related info. */ - if (status = krb5_read_message((krb5_pointer)&netf, &inbuf)) - fatal(netf, "Error reading message"); - if (inbuf.length) - fatal(netf, "Forwarding is not yet supported"); - if (status = krb5_read_message((krb5_pointer)&netf, &inbuf)) - fatal(netf, "Error reading message"); - if (inbuf.length) - fatal(netf, "Forwarding is not yet supported"); - - getstr(lusername, sizeof(lusername), "locuser"); - getstr(term, sizeof(term), "Terminal type"); - rusername = malloc(sizeof (lusername) + 1); - getstr(rusername, sizeof(lusername), "remuser"); - /* OK we have authenticated this user - now check authorization. */ - /* We must do this here since we want the same functionality as */ - /* the MIT version without having to provide the login.krb program.*/ - - /* The Kerberos authenticated programs must use krb5_kuserok */ - - krb5_unparse_name(kdata->client,&krusername); + /* The Kerberos authenticated programs must use krb5_kuserok or kuserok*/ if (must_pass_k5 || must_pass_one) { +#ifdef ALWAYS_V5_KUSEROK /* krb5_kuserok returns 1 if OK */ - rc = !(krb5_kuserok(kdata->client,lusername)); - - if (rc){ + if (!client || !krb5_kuserok(client, lusername)){ + if (ticket) + krb5_free_ticket(ticket); syslog(LOG_ERR, "Principal %s (%s@%s) logging in as %s failed krb5_kuserok.\n", - krusername, rusername, host, lusername); + krusername ? krusername : "", rusername, host, lusername); if (must_pass_k5) fatal(netf, "Permission denied"); failed_k5 = 1; - if (ticket) - krb5_free_ticket(ticket); } +#else + if (V4) { + /* kuserok returns 0 if OK */ + if (kuserok(v4_kdata, lusername)){ + syslog(LOG_ERR, + "Principal %s (%s@%s) logging in as %s failed kuserok.\n", + krusername ? krusername : "", rusername, host, + lusername); + if (must_pass_k5) + fatal(netf, "Permission denied"); + failed_k5 = 1; + } + } + else { + /* krb5_kuserok returns 1 if OK */ + if (!client || !krb5_kuserok(client, lusername)){ + if (ticket) + krb5_free_ticket(ticket); + syslog(LOG_ERR, + "Principal %s (%s@%s) logging in as %s failed krb5_kuserok.\n", + krusername ? krusername : "", rusername, host, + lusername); + if (must_pass_k5) + fatal(netf, "Permission denied"); + failed_k5 = 1; + } + } +#endif } /* The kerberos authenticated request must pass ruserok also if asked for. */ if (must_pass_rhosts || (failed_k5 && must_pass_one)) { + /* Cannot check .rhosts unless connection from a privileged port. */ + if (non_privileged) + fatal(netf, "Permission denied - Connection from bad port"); + pwd = (struct passwd *) getpwnam(lusername); if ((pwd == (struct passwd *) 0) || (ruserok(rhost_name, pwd->pw_uid == 0, rusername, lusername))) { @@ -1089,11 +1096,11 @@ do_krb_login(host) if (pwd == (struct passwd *) 0) syslog(LOG_ERR, "Principal %s (%s@%s) logging in as %s has no account.\n", - krusername, rusername, rhost_name, lusername); + krusername ? krusername : "", rusername, rhost_name, lusername); else syslog(LOG_ERR, "Principal %s (%s@%s) logging in as %s failed ruserok.\n", - krusername, rusername, rhost_name, lusername); + krusername ? krusername : "", rusername, rhost_name, lusername); if (must_pass_rhosts) fatal(netf, "Permission denied"); @@ -1105,7 +1112,8 @@ do_krb_login(host) -getstr(buf, cnt, err) +getstr(fd, buf, cnt, err) + int fd; char *buf; int cnt; char *err; @@ -1114,7 +1122,7 @@ getstr(buf, cnt, err) char c; do { - if (read(0, &c, 1) != 1) { + if (read(fd, &c, 1) != 1) { exit(1); } if (--cnt < 0) { @@ -1131,8 +1139,8 @@ char storage[2*BUFSIZ]; /* storage for the decryption */ int nstored = 0; char *store_ptr = storage; - -des_read(fd, buf, len) +int +v5_des_read(fd, buf, len) int fd; register char *buf; int len; @@ -1222,8 +1230,8 @@ des_read(fd, buf, len) } - -des_write(fd, buf, len) +int +v5_des_write(fd, buf, len) int fd; char *buf; int len; @@ -1260,7 +1268,6 @@ des_write(fd, buf, len) } else return(len); } - #endif /* KERBEROS */ @@ -1319,9 +1326,10 @@ void usage() { #ifdef KERBEROS syslog(LOG_ERR, - "usage: klogind [-rRkKxpP] or [r/R][k/K][x/e][p/P]logind"); + "usage: klogind [-rRkKxpP] [-D port] or [r/R][k/K][x/e][p/P]logind"); #else - syslog(LOG_ERR, "usage: rlogind [-rRpP] or [r/R][p/P]logind"); + syslog(LOG_ERR, + "usage: rlogind [-rRpP] [-D port] or [r/R][p/P]logind"); #endif } @@ -1362,4 +1370,739 @@ int default_realm(principal) free(def_realm); return 1; } + + +#ifndef KRB_SENDAUTH_VLEN +#define KRB_SENDAUTH_VLEN 8 /* length for version strings */ #endif + +#define KRB_SENDAUTH_VERS "AUTHV0.1" /* MUST be KRB_SENDAUTH_VLEN + chars */ + +krb5_error_code +recvauth() +{ + krb5_error_code status; + struct sockaddr_in peersin; + char hostname[100]; + char krb_vers[KRB_SENDAUTH_VLEN + 1]; + int len; + + len = sizeof(peersin); + if (getpeername(netf, (struct sockaddr *)&peersin, &len)) { + syslog(LOG_ERR, "get peer name failed %d", netf); + exit(1); + } + + len = sizeof(int); + if ((status = krb5_net_read(netf, krb_vers, len)) != len) + return((status < 0) ? errno : ECONNABORTED); + + krb_vers[len] = '\0'; + + if (!strncmp(krb_vers, KRB_SENDAUTH_VERS, len)) { + /* Must be V4 rlogin client */ +#ifdef SERVE_V4 + char version[9]; + struct sockaddr_in faddr; + char instance[INST_SZ]; + long authoptions; + + V4 = 1; /* Set flag for rest of code */ + + des_read = v4_des_read; + des_write = v4_des_write; + + if (do_encrypt) + authoptions = KOPT_DO_MUTUAL; + else + authoptions = 0L; + + len = sizeof(faddr); + if (getsockname(netf, (struct sockaddr *)&faddr, &len)) { + exit(1); + } + + v4_kdata = (AUTH_DAT *)malloc( sizeof(AUTH_DAT) ); + v4_ticket = (KTEXT) malloc(sizeof(KTEXT_ST)); + + strcpy(instance, "*"); + + if (status = v4_recvauth(krb_vers, authoptions, netf, v4_ticket, + "rcmd", instance, + &peersin, &faddr, + v4_kdata, "", + schedule, version)) { + return(status); + } + + getstr(netf, lusername, sizeof (lusername), "locuser"); + getstr(netf, term, sizeof(term), "Terminal type"); + /* We do not really know the remote user's login name. + * Assume it to be the same as the first component of the + * principal's name. + */ + strcpy(rusername, v4_kdata->pname); + krusername = (char *) malloc(strlen(v4_kdata->pname) + 1 + + strlen(v4_kdata->pinst) + 1 + + strlen(v4_kdata->prealm) + 1); + sprintf(krusername, "%s/%s@%s", v4_kdata->pname, + v4_kdata->pinst, v4_kdata->prealm); + + if (status = krb5_parse_name(krusername, &client)) + return(status); +#else + syslog(LOG_ERR, "Kerberos V4 authentication: rejected!"); + fatal(netf, "Permission denied"); +#endif + } + else if (isprint(krb_vers[0])) { + /* Un-kerberized rlogin client */ +#ifdef SERVE_NON_KRB + des_read = read; + des_write = write; + + strncpy(rusername, krb_vers, sizeof(int)); + getstr(netf, rusername+4, sizeof(rusername)-sizeof(int), "remuser"); + getstr(netf, lusername, sizeof(lusername), "locuser"); + getstr(netf, term, sizeof(term), "Terminal type"); +#else + syslog(LOG_ERR, "Un-kerberized client: authentication rejected!"); + fatal(netf, "Permission denied"); +#endif + } + else { + /* Must be V5 rlogin client */ + krb5_principal server; + krb5_address peeraddr; + krb5_data inbuf; + + des_read = v5_des_read; + des_write = v5_des_write; + + /* + * First read the sendauth version string and check it. + */ + inbuf.length = ntohl(*((int *) krb_vers)); + + if (inbuf.length < 0 || inbuf.length > 25) + return 255; + + if (!(inbuf.data = malloc(inbuf.length))) { + return(ENOMEM); + } + + if ((len = krb5_net_read(netf, inbuf.data, inbuf.length)) != + inbuf.length) { + xfree(inbuf.data); + return((len < 0) ? errno : ECONNABORTED); + } + + if (strcmp(inbuf.data, "KRB5_SENDAUTH_V1.0")) { + xfree(inbuf.data); + status = KRB5_SENDAUTH_BADAUTHVERS; + return status; + } + xfree(inbuf.data); + +#ifdef unicos61 +#define SIZEOF_INADDR SIZEOF_in_addr +#else +#define SIZEOF_INADDR sizeof(struct in_addr) +#endif + + peeraddr.addrtype = peersin.sin_family; + peeraddr.length = SIZEOF_INADDR; + peeraddr.contents = (krb5_octet *)&peersin.sin_addr; + + gethostname(hostname, 100); + if (status = krb5_sname_to_principal(hostname,"host", KRB5_NT_SRV_HST, + &server)) { + syslog(LOG_ERR, "parse server name %s: %s", "host", + error_message(status)); + exit(1); + } + krb5_princ_type(server) = KRB5_NT_SRV_HST; + + if (status = v5_recvauth(&netf, + "KCMDV0.1", + server, /* Specify daemon principal */ + &peeraddr, /* We do want to match this + against caddrs in the + ticket. */ + 0, /* use v5srvtab */ + 0, /* no keyproc */ + 0, /* no keyproc arg */ + 0, /* no rc_type */ + 0, /* no seq number */ + &client, /* return client */ + &ticket, /* return ticket */ + &kdata /* return authenticator */ + )) { + /* Dummy forwarding reads */ + if (status = krb5_read_message((krb5_pointer)&netf, &inbuf)) + fatal(netf, "Error reading message"); + if (status = krb5_read_message((krb5_pointer)&netf, &inbuf)) + fatal(netf, "Error reading message"); + + /* Dont exit out for klogin, but + grab locuser, terminal, and remuser. + */ + getstr(netf, lusername, sizeof(lusername), "locuser"); + getstr(netf, term, sizeof(term), "Terminal type"); + getstr(netf, rusername, sizeof(rusername), "remuser"); + return status; + } + + if (status = krb5_unparse_name(client, &krusername)) + return status; + + /* Setup up eblock if encrypted login session */ + /* otherwise zero out session key */ + if (do_encrypt) { + krb5_use_keytype(&eblock, + ticket->enc_part2->session->keytype); + if (status = krb5_process_key(&eblock, + ticket->enc_part2->session)) + fatal(netf, "Permission denied"); + } + + getstr(netf, lusername, sizeof(lusername), "locuser"); + getstr(netf, term, sizeof(term), "Terminal type"); + getstr(netf, rusername, sizeof(rusername), "remuser"); + + if (status = krb5_read_message((krb5_pointer)&netf, &inbuf)) + fatal(netf, "Error reading message"); + + if (inbuf.length) { /* Forwarding being done, read creds */ + if (status = rd_and_store_for_creds(&inbuf, ticket, lusername)) + fatal(netf, "Can't get forwarded credentials"); + } + } + return 0; +} + + +#ifdef SERVE_V4 + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif /* max */ + +krb5_error_code +v4_recvauth(krb_vers, options, fd, ticket, service, instance, faddr, + laddr, kdata, filename, schedule, version) + char *krb_vers; + long options; /* bit-pattern of options */ + int fd; /* file descr. to read from */ + KTEXT ticket; /* storage for client's ticket */ + char *service; /* service expected */ + char *instance; /* inst expected (may be filled in) */ + struct sockaddr_in *faddr; /* address of foreign host on fd */ + struct sockaddr_in *laddr; /* local address */ + AUTH_DAT *kdata; /* kerberos data (returned) */ + char *filename; /* name of file with service keys */ + Key_schedule schedule; /* key schedule (return) */ + char *version; /* version string (filled in) */ +{ + + int i, cc; + char *cp; + int rem; + long tkt_len, priv_len; + u_long cksum; + u_char tmp_buf[MAX_KTXT_LEN+max(KRB_SENDAUTH_VLEN+1,21)]; + + /* read the protocol version number */ + if (krb_net_read(fd, krb_vers+sizeof(int), + KRB_SENDAUTH_VLEN-sizeof(int)) != + KRB_SENDAUTH_VLEN-sizeof(int)) + return(errno); + krb_vers[KRB_SENDAUTH_VLEN] = '\0'; + + /* check version string */ + if (strcmp(krb_vers,KRB_SENDAUTH_VERS)) { + return(KFAILURE); + } else { + /* read the application version string */ + if (krb_net_read(fd, version, KRB_SENDAUTH_VLEN) != + KRB_SENDAUTH_VLEN) + return(errno); + version[KRB_SENDAUTH_VLEN] = '\0'; + + /* get the length of the ticket */ + if (krb_net_read(fd, (char *)&tkt_len, sizeof(tkt_len)) != + sizeof(tkt_len)) + return(errno); + + /* sanity check */ + ticket->length = ntohl((unsigned long)tkt_len); + if ((ticket->length <= 0) || (ticket->length > MAX_KTXT_LEN)) { + if (options & KOPT_DO_MUTUAL) { + rem = KFAILURE; + goto mutual_fail; + } else + return(KFAILURE); /* XXX there may still be junk on the fd? */ + } + + /* read the ticket */ + if (krb_net_read(fd, (char *) ticket->dat, ticket->length) + != ticket->length) + return(errno); + } + /* + * now have the ticket. decrypt it to get the authenticated + * data. + */ + if (rem = krb_rd_req(ticket,service,instance,faddr->sin_addr.s_addr, + kdata,filename)) + return(KFAILURE); + + /* if we are doing mutual auth (used by erlogin), compose a response */ + if (options & KOPT_DO_MUTUAL) { + if (rem != KSUCCESS) + /* the krb_rd_req failed */ + goto mutual_fail; + + /* add one to the (formerly) sealed checksum, and re-seal it + for return to the client */ + cksum = kdata->checksum + 1; + cksum = htonl(cksum); +#ifdef CRYPT + key_sched(kdata->session,schedule); +#endif + priv_len = krb_mk_priv((unsigned char *)&cksum, + tmp_buf, + (unsigned long) sizeof(cksum), + schedule, + kdata->session, + laddr, + faddr); + if (priv_len < 0) { + /* re-sealing failed; notify the client */ + rem = KFAILURE; /* XXX */ + mutual_fail: + priv_len = -1; + tkt_len = htonl((unsigned long) priv_len); + /* a length of -1 is interpreted as an authentication + failure by the client */ + if ((cc = krb_net_write(fd, (char *)&tkt_len, sizeof(tkt_len))) + != sizeof(tkt_len)) + return(cc); + return(rem); + } else { + /* re-sealing succeeded, send the private message */ + tkt_len = htonl((unsigned long)priv_len); + if ((cc = krb_net_write(fd, (char *)&tkt_len, sizeof(tkt_len))) + != sizeof(tkt_len)) + return(cc); + if ((cc = krb_net_write(fd, (char *)tmp_buf, (int) priv_len)) + != (int) priv_len) + return(cc); + } + } + return(0); +} + +#endif /* SERVE_V4 */ + +extern krb5_flags krb5_kdc_default_options; + +krb5_error_code +v5_recvauth(/* IN */ + fd, appl_version, server, sender_addr, fetch_from, + keyproc, keyprocarg, rc_type, + /* OUT */ + seq_number, client, ticket, authent) + krb5_pointer fd; + char *appl_version; + krb5_principal server; + krb5_address *sender_addr; + krb5_pointer fetch_from; + krb5_int32 *seq_number; + char *rc_type; + krb5_rdreq_key_proc keyproc; + krb5_pointer keyprocarg; + krb5_principal *client; + krb5_ticket **ticket; + krb5_authenticator **authent; +{ + krb5_error_code retval, problem; + krb5_data inbuf; + krb5_tkt_authent *authdat; + krb5_data outbuf; + krb5_rcache rcache; + krb5_octet response; + krb5_data *server_name; + char *cachename; + extern krb5_deltat krb5_clockskew; + static char *rc_base = "rc_"; + + /* + * Zero out problem variable. If problem is set at the end of + * the intial version negotiation section, it means that we + * need to send an error code back to the client application + * and exit. + */ + problem = 0; + + /* + * Read and check the application version string. + */ + if (retval = krb5_read_message(fd, &inbuf)) + return(retval); + if (strcmp(inbuf.data, appl_version)) { + xfree(inbuf.data); + if (!problem) + problem = KRB5_SENDAUTH_BADAPPLVERS; + } + xfree(inbuf.data); + /* + * OK, now check the problem variable. If it's zero, we're + * fine and we can continue. Otherwise, we have to signal an + * error to the client side and bail out. + */ + switch (problem) { + case 0: + response = 0; + break; + case KRB5_SENDAUTH_BADAUTHVERS: + response = 1; + break; + case KRB5_SENDAUTH_BADAPPLVERS: + response = 2; + break; + default: + /* + * Should never happen! + */ + response = 255; +#ifdef SENDAUTH_DEBUG + fprintf(stderr, "Programming botch in recvauth! problem = %d", + problem); + abort(); +#endif + break; + } + + /* + * Now we actually write the response. If the response is non-zero, + * exit with a return value of problem + */ + if ((krb5_net_write(*((int *) fd), (char *)&response, 1)) < 0) { + return(problem); /* We'll return the top-level problem */ + } + if (problem) + return(problem); + rcache = NULL; +#ifdef WORKING_RCACHE + /* + * Setup the replay cache. + */ + if (!(rcache = (krb5_rcache) malloc(sizeof(*rcache)))) + problem = ENOMEM; + if (!problem) + problem = krb5_rc_resolve_type(&rcache, + rc_type ? rc_type : "dfl"); + cachename = NULL; + server_name = krb5_princ_component(server, 0); + if (!problem && !(cachename = malloc(server_name->length+1+strlen(rc_base)))) + problem = ENOMEM; + if (!problem) { + strcpy(cachename, rc_base ? rc_base : "rc_"); + strncat(cachename, server_name->data, server_name->length); + cachename[server_name->length+strlen(rc_base)] = '\0'; + problem = krb5_rc_resolve(rcache, cachename); + } + if (!problem) { + if (krb5_rc_recover(rcache)) + /* + * If the rc_recover didn't work, then try + * initializing the replay cache. + */ + problem = krb5_rc_initialize(rcache, krb5_clockskew); + if (problem) { + krb5_rc_close(rcache); + rcache = NULL; + } + } +#endif + + /* + * Now, let's read the AP_REQ message and decode it + */ + if (retval = krb5_read_message(fd, &inbuf)) { +#ifdef WORKING_RCACHE + (void) krb5_rc_close(rcache); + if (cachename) + free(cachename); +#endif + return(retval); + } + authdat = 0; /* so we can tell if we need to + free it later... */ + if (!problem) + problem = krb5_rd_req(&inbuf, server, sender_addr, fetch_from, + keyproc, keyprocarg, rcache, &authdat); + xfree(inbuf.data); +#ifdef WORKING_RCACHE + if (rcache) + retval = krb5_rc_close(rcache); +#endif + if (!problem && retval) + problem = retval; +#ifdef WORKING_RCACHE + if (cachename) + free(cachename); +#endif + + /* + * If there was a problem, send back a krb5_error message, + * preceeded by the length of the krb5_error message. If + * everything's ok, send back 0 for the length. + */ + if (problem) { + krb5_error error; + const char *message; + + memset((char *)&error, 0, sizeof(error)); + krb5_us_timeofday(&error.stime, &error.susec); + error.server = server; + error.error = problem - ERROR_TABLE_BASE_krb5; + if (error.error > 127) + error.error = KRB_ERR_GENERIC; + message = error_message(problem); + error.text.length = strlen(message) + 1; + if (!(error.text.data = malloc(error.text.length))) + return(ENOMEM); + strcpy(error.text.data, message); + if (retval = krb5_mk_error(&error, &outbuf)) { + free(error.text.data); + return(retval); + } + free(error.text.data); + } else { + outbuf.length = 0; + outbuf.data = 0; + } + if (retval = krb5_write_message(fd, &outbuf)) { + if (outbuf.data) + xfree(outbuf.data); + if (!problem) + krb5_free_tkt_authent(authdat); + return(retval); + } + if (problem) { + /* + * We sent back an error, we need to return + */ + if (authdat) krb5_free_tkt_authent(authdat); + return(problem); + } + /* + * Here lies the mutual authentication stuff... + * + * We're going to compose and send a AP_REP message. + */ + if ((authdat->ap_options & AP_OPTS_MUTUAL_REQUIRED)) { + krb5_ap_rep_enc_part repl; + + /* + * Generate a random sequence number + */ + if (seq_number && + (retval = krb5_generate_seq_number(authdat->ticket->enc_part2->session, + seq_number))) { + krb5_free_tkt_authent(authdat); + return(retval); + } + + repl.ctime = authdat->authenticator->ctime; + repl.cusec = authdat->authenticator->cusec; + repl.subkey = authdat->authenticator->subkey; + if (seq_number) + repl.seq_number = *seq_number; + else + repl.seq_number = 0; + + if (retval = krb5_mk_rep(&repl, + authdat->ticket->enc_part2->session, + &outbuf)) { + krb5_free_tkt_authent(authdat); + return(retval); + } + if (retval = krb5_write_message(fd, &outbuf)) { + xfree(outbuf.data); + krb5_free_tkt_authent(authdat); + return(retval); + } + xfree(outbuf.data); + } + /* + * At this point, we've won. We just need to copy whatever + * parts of the authdat structure which the user wants, clean + * up, and exit. + */ + if (client) + if (retval = + krb5_copy_principal(authdat->ticket->enc_part2->client, + client)) + return(retval); + /* + * The following efficiency hack assumes knowledge about the + * structure of krb5_tkt_authent. If we later add additional + * allocated substructures to krb5_tkt_authent, they will have + * to be reflected here; otherwise, we will probably have a + * memory leak. + * + * If the user wants that part of the authdat structure, + * return it; otherwise free it. + */ + if (ticket) + *ticket = authdat->ticket; + else + krb5_free_ticket(authdat->ticket); + if (authent) + *authent = authdat->authenticator; + else + krb5_free_authenticator(authdat->authenticator); + xfree(authdat); + return 0; +} + +#ifdef SERVE_V4 + +int +v4_des_read(fd, buf, len) +int fd; +register char *buf; +int len; +{ + int nreturned = 0; + long net_len, rd_len; + int cc; + + if (!do_encrypt) + return(read(fd, buf, len)); + + if (nstored >= len) { + bcopy(store_ptr, buf, len); + store_ptr += len; + nstored -= len; + return(len); + } else if (nstored) { + bcopy(store_ptr, buf, nstored); + nreturned += nstored; + buf += nstored; + len -= nstored; + nstored = 0; + } + + if ((cc = krb_net_read(fd, &net_len, sizeof(net_len))) != sizeof(net_len)) { + /* XXX can't read enough, pipe + must have closed */ + return(0); + } + net_len = ntohl(net_len); + if (net_len < 0 || net_len > sizeof(des_inbuf)) { + /* XXX preposterous length, probably out of sync. + act as if pipe closed */ + return(0); + } + /* the writer tells us how much real data we are getting, but + we need to read the pad bytes (8-byte boundary) */ + rd_len = roundup(net_len, 8); + if ((cc = krb_net_read(fd, des_inbuf, rd_len)) != rd_len) { + /* XXX can't read enough, pipe + must have closed */ + return(0); + } + (void) pcbc_encrypt(des_inbuf, + storage, + (net_len < 8) ? 8 : net_len, + schedule, + v4_kdata->session, + DECRYPT); + /* + * when the cleartext block is < 8 bytes, it is "right-justified" + * in the block, so we need to adjust the pointer to the data + */ + if (net_len < 8) + store_ptr = storage + 8 - net_len; + else + store_ptr = storage; + nstored = net_len; + if (nstored > len) { + bcopy(store_ptr, buf, len); + nreturned += len; + store_ptr += len; + nstored -= len; + } else { + bcopy(store_ptr, buf, nstored); + nreturned += nstored; + nstored = 0; + } + + return(nreturned); +} + +int +v4_des_write(fd, buf, len) +int fd; +char *buf; +int len; +{ + long net_len; + static int seeded = 0; + static char garbage_buf[8]; + long garbage; + + if (!do_encrypt) + return(write(fd, buf, len)); + + /* + * pcbc_encrypt outputs in 8-byte (64 bit) increments + * + * it zero-fills the cleartext to 8-byte padding, + * so if we have cleartext of < 8 bytes, we want + * to insert random garbage before it so that the ciphertext + * differs for each transmission of the same cleartext. + * if len < 8 - sizeof(long), sizeof(long) bytes of random + * garbage should be sufficient; leave the rest as-is in the buffer. + * if len > 8 - sizeof(long), just garbage fill the rest. + */ + +#ifdef min +#undef min +#endif +#define min(a,b) ((a < b) ? a : b) + + if (len < 8) { + if (!seeded) { + seeded = 1; + srandom((int) time((long *)0)); + } + garbage = random(); + /* insert random garbage */ + (void) bcopy(&garbage, garbage_buf, min(sizeof(long),8)); + + /* this "right-justifies" the data in the buffer */ + (void) bcopy(buf, garbage_buf + 8 - len, len); + } + (void) pcbc_encrypt((len < 8) ? garbage_buf : buf, + des_outbuf, + (len < 8) ? 8 : len, + schedule, + v4_kdata->session, + ENCRYPT); + + /* tell the other end the real amount, but send an 8-byte padded + packet */ + net_len = htonl(len); + (void) write(fd, &net_len, sizeof(net_len)); + (void) write(fd, des_outbuf, roundup(len,8)); + return(len); +} + +#endif /* SERVE_V4 */ +#endif /* KERBEROS */