* 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 <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/file.h>
+#include <ctype.h>
+
/* #include <sys/unistd.h> ??? What system has a sys/unistd.h? */
#include <netinet/in.h>
#ifdef KERBEROS
#include <krb5/krb5.h>
+#include <krb5/osconf.h>
#include <krb5/asn1.h>
#include <krb5/crc-32.h>
#include <krb5/mit-des.h>
#include <krb5/los-proto.h>
-#include <com_err.h>
-
#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 <kerberosIV/krb.h>
+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 <com_err.h>
#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;
#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"
#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();
#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;
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;
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') {
case 'P': /* passwd is a must */
passwd_req = 1;
break;
+ case 'D':
+ debug_port = atoi(optarg);
+ break;
case '?':
default:
usage();
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);
}
int child;
int cleanup();
int netf;
-krb5_principal client;
char line[MAXPATHLEN];
extern char *inet_ntoa();
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);
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);
#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);
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);
}
#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 **/
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 */
sprintf(buferror,"Cannot write slave pty %s ",line);
fatalperror(f,buferror,errno);
}
-#endif /* KERBEROS */
protocol(f, p);
signal(SIGCHLD, SIG_IGN);
}
}
if (ibits & (1<<f)) {
- fcc = des_read(f, fibuf, sizeof (fibuf));
+ fcc = (*des_read)(f, fibuf, sizeof (fibuf));
if (fcc < 0 && errno == EWOULDBLOCK)
fcc = 0;
else {
}
}
if ((obits & (1<<f)) && pcc > 0) {
- cc = des_write(f, pbp, pcc);
+ cc = (*des_write)(f, pbp, pcc);
if (cc < 0 && errno == EWOULDBLOCK) {
/* also shouldn't happen */
sleep(5);
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);
#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))) {
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");
-getstr(buf, cnt, err)
+getstr(fd, buf, cnt, err)
+ int fd;
char *buf;
int cnt;
char *err;
char c;
do {
- if (read(0, &c, 1) != 1) {
+ if (read(fd, &c, 1) != 1) {
exit(1);
}
if (--cnt < 0) {
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;
}
-
-des_write(fd, buf, len)
+int
+v5_des_write(fd, buf, len)
int fd;
char *buf;
int len;
}
else return(len);
}
-
#endif /* KERBEROS */
{
#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
}
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 */