#include "k5-int.h"
#if !defined(_MACINTOSH)
#ifdef KRB5_KRB4_COMPAT
#include <kerberosIV/krb.h>
#endif
#include "com_err.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "defines.h"
#ifdef KRB5_KRB4_COMPAT
static int krb_v4_recvauth(long options, int fd, KTEXT ticket,
char *service, char *instance,
struct sockaddr_in *faddr,
struct sockaddr_in *laddr,
AUTH_DAT *kdata,
char *filename,
Key_schedule schedule,
char *version);
#endif
#define KRB_V4_SENDAUTH_VERS "AUTHV0.1"
#define KRB_V5_SENDAUTH_VERS "KRB5_SENDAUTH_V1.0"
#define KRB5_RECVAUTH_V4 4
#define KRB5_RECVAUTH_V5 5
#ifdef KRB5_KRB4_COMPAT
krb5_error_code
krb5_compat_recvauth(context, auth_context,
fdp, appl_version, server, flags, keytab,
v4_options, v4_service, v4_instance, v4_faddr, v4_laddr,
v4_filename,
ticket,
auth_sys, v4_kdata, v4_schedule, v4_version)
krb5_context context;
krb5_auth_context *auth_context;
krb5_pointer fdp;
char *appl_version;
krb5_principal server;
krb5_int32 flags;
krb5_keytab keytab;
krb5_ticket ** ticket;
krb5_int32 *auth_sys;
krb5_int32 v4_options;
char *v4_service;
char *v4_instance;
struct sockaddr_in *v4_faddr;
struct sockaddr_in *v4_laddr;
AUTH_DAT **v4_kdata;
char *v4_filename;
Key_schedule v4_schedule;
char *v4_version;
{
union verslen {
krb5_int32 len;
char vers[4];
} vers;
char *buf;
int len, length;
krb5_int32 retval;
int fd = *( (int *) fdp);
#ifdef KRB5_KRB4_COMPAT
KTEXT v4_ticket;
#endif
if ((retval = krb5_net_read(context, fd, vers.vers, 4)) != 4)
return((retval < 0) ? errno : ECONNABORTED);
#ifdef KRB5_KRB4_COMPAT
if (!strncmp(vers.vers, KRB_V4_SENDAUTH_VERS, 4)) {
if ((retval = krb5_net_read(context, fd, vers.vers, 4)) != 4)
return((retval < 0) ? errno : ECONNABORTED);
if (strncmp(vers.vers, KRB_V4_SENDAUTH_VERS+4, 4))
return KRB5_SENDAUTH_BADAUTHVERS;
*auth_sys = KRB5_RECVAUTH_V4;
*v4_kdata = (AUTH_DAT *) malloc( sizeof(AUTH_DAT) );
v4_ticket = (KTEXT) malloc(sizeof(KTEXT_ST));
retval = krb_v4_recvauth(v4_options, fd, v4_ticket,
v4_service, v4_instance, v4_faddr,
v4_laddr, *v4_kdata, v4_filename,
v4_schedule, v4_version);
krb5_xfree(v4_ticket);
switch (retval) {
case RD_AP_OK:
return 0;
case RD_AP_TIME:
return KRB5KRB_AP_ERR_SKEW;
case RD_AP_EXP:
return KRB5KRB_AP_ERR_TKT_EXPIRED;
case RD_AP_NYV:
return KRB5KRB_AP_ERR_TKT_NYV;
case RD_AP_NOT_US:
return KRB5KRB_AP_ERR_NOT_US;
case RD_AP_UNDEC:
return KRB5KRB_AP_ERR_BAD_INTEGRITY;
case RD_AP_REPEAT:
return KRB5KRB_AP_ERR_REPEAT;
case RD_AP_MSG_TYPE:
return KRB5KRB_AP_ERR_MSG_TYPE;
case RD_AP_MODIFIED:
return KRB5KRB_AP_ERR_MODIFIED;
case RD_AP_ORDER:
return KRB5KRB_AP_ERR_BADORDER;
case RD_AP_BADD:
return KRB5KRB_AP_ERR_BADADDR;
default:
return KRB5_SENDAUTH_BADRESPONSE;
}
}
#endif
len = (int) ntohl(vers.len);
if (len < 0 || len > 255)
return KRB5_SENDAUTH_BADAUTHVERS;
buf = malloc((unsigned) len);
if (!buf)
return ENOMEM;
length = krb5_net_read(context, fd, buf, len);
if (len != length) {
krb5_xfree(buf);
if (len < 0)
return errno;
else
return ECONNABORTED;
}
if (strcmp(buf, KRB_V5_SENDAUTH_VERS)) {
krb5_xfree(buf);
return KRB5_SENDAUTH_BADAUTHVERS;
}
krb5_xfree(buf);
*auth_sys = KRB5_RECVAUTH_V5;
retval = krb5_recvauth(context, auth_context, fdp, appl_version, server,
flags | KRB5_RECVAUTH_SKIP_VERSION,
keytab, ticket);
return retval;
}
krb5_error_code
krb5_compat_recvauth_version(context, auth_context,
fdp, server, flags, keytab,
v4_options, v4_service, v4_instance, v4_faddr,
v4_laddr,
v4_filename,
ticket,
auth_sys, v4_kdata, v4_schedule,
version)
krb5_context context;
krb5_auth_context *auth_context;
krb5_pointer fdp;
krb5_principal server;
krb5_int32 flags;
krb5_keytab keytab;
krb5_ticket ** ticket;
krb5_int32 *auth_sys;
krb5_int32 v4_options;
char *v4_service;
char *v4_instance;
struct sockaddr_in *v4_faddr;
struct sockaddr_in *v4_laddr;
AUTH_DAT **v4_kdata;
char *v4_filename;
Key_schedule v4_schedule;
krb5_data *version;
{
union verslen {
krb5_int32 len;
char vers[4];
} vers;
char *buf;
int len, length;
krb5_int32 retval;
int fd = *( (int *) fdp);
#ifdef KRB5_KRB4_COMPAT
KTEXT v4_ticket;
#endif
if ((retval = krb5_net_read(context, fd, vers.vers, 4)) != 4)
return((retval < 0) ? errno : ECONNABORTED);
#ifdef KRB5_KRB4_COMPAT
if (v4_faddr->sin_family == AF_INET
&& !strncmp(vers.vers, KRB_V4_SENDAUTH_VERS, 4)) {
if ((retval = krb5_net_read(context, fd, vers.vers, 4)) != 4)
return((retval < 0) ? errno : ECONNABORTED);
if (strncmp(vers.vers, KRB_V4_SENDAUTH_VERS+4, 4))
return KRB5_SENDAUTH_BADAUTHVERS;
*auth_sys = KRB5_RECVAUTH_V4;
*v4_kdata = (AUTH_DAT *) malloc( sizeof(AUTH_DAT) );
v4_ticket = (KTEXT) malloc(sizeof(KTEXT_ST));
version->length = KRB_SENDAUTH_VLEN;
version->data = malloc (KRB_SENDAUTH_VLEN + 1);
version->data[KRB_SENDAUTH_VLEN] = 0;
if (version->data == 0)
return errno;
retval = krb_v4_recvauth(v4_options, fd, v4_ticket,
v4_service, v4_instance, v4_faddr,
v4_laddr, *v4_kdata, v4_filename,
v4_schedule, version->data);
krb5_xfree(v4_ticket);
switch (retval) {
case RD_AP_OK:
return 0;
case RD_AP_TIME:
return KRB5KRB_AP_ERR_SKEW;
case RD_AP_EXP:
return KRB5KRB_AP_ERR_TKT_EXPIRED;
case RD_AP_NYV:
return KRB5KRB_AP_ERR_TKT_NYV;
case RD_AP_NOT_US:
return KRB5KRB_AP_ERR_NOT_US;
case RD_AP_UNDEC:
return KRB5KRB_AP_ERR_BAD_INTEGRITY;
case RD_AP_REPEAT:
return KRB5KRB_AP_ERR_REPEAT;
case RD_AP_MSG_TYPE:
return KRB5KRB_AP_ERR_MSG_TYPE;
case RD_AP_MODIFIED:
return KRB5KRB_AP_ERR_MODIFIED;
case RD_AP_ORDER:
return KRB5KRB_AP_ERR_BADORDER;
case RD_AP_BADD:
return KRB5KRB_AP_ERR_BADADDR;
default:
return KRB5_SENDAUTH_BADRESPONSE;
}
}
#endif
len = (int) ntohl(vers.len);
if (len < 0 || len > 255)
return KRB5_SENDAUTH_BADAUTHVERS;
buf = malloc((unsigned) len);
if (!buf)
return ENOMEM;
length = krb5_net_read(context, fd, buf, len);
if (len != length) {
krb5_xfree(buf);
if (len < 0)
return errno;
else
return ECONNABORTED;
}
if (strcmp(buf, KRB_V5_SENDAUTH_VERS)) {
krb5_xfree(buf);
return KRB5_SENDAUTH_BADAUTHVERS;
}
krb5_xfree(buf);
*auth_sys = KRB5_RECVAUTH_V5;
retval = krb5_recvauth_version(context, auth_context, fdp, server,
flags | KRB5_RECVAUTH_SKIP_VERSION,
keytab, ticket, version);
return retval;
}
#endif
#ifndef max
#define max(a,b) (((a) > (b)) ? (a) : (b))
#endif
#ifdef KRB5_KRB4_COMPAT
static int
krb_v4_recvauth(options, fd, ticket, service, instance, faddr, laddr, kdata,
filename, schedule, version)
long options;
int fd;
KTEXT ticket;
char *service;
char *instance;
struct sockaddr_in *faddr;
struct sockaddr_in *laddr;
AUTH_DAT *kdata;
char *filename;
Key_schedule schedule;
char *version;
{
int cc, old_vers = 0;
int rem;
krb5_int32 tkt_len, priv_len;
krb5_ui_4 cksum;
u_char tmp_buf[MAX_KTXT_LEN+max(KRB_SENDAUTH_VLEN+1,21)];
if ((krb_net_read(fd, version, KRB_SENDAUTH_VLEN) !=
KRB_SENDAUTH_VLEN))
return(errno);
version[KRB_SENDAUTH_VLEN] = '\0';
if (krb_net_read(fd, (char *)&tkt_len, sizeof(tkt_len)) !=
sizeof(tkt_len))
return(errno);
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);
}
if (krb_net_read(fd, (char *) ticket->dat, ticket->length)
!= ticket->length)
return(errno);
rem = krb_rd_req(ticket,service,instance,faddr->sin_addr.s_addr,
kdata,filename);
if (old_vers) return(rem);
if (options & KOPT_DO_MUTUAL) {
if (rem != KSUCCESS)
goto mutual_fail;
cksum = kdata->checksum + 1;
cksum = htonl(cksum);
#ifndef NOENCRYPTION
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) {
rem = KFAILURE;
mutual_fail:
priv_len = -1;
tkt_len = htonl((unsigned long) priv_len);
if ((cc = krb_net_write(fd, (char *)&tkt_len, sizeof(tkt_len)))
!= sizeof(tkt_len))
return(cc);
return(rem);
} else {
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(rem);
}
#endif
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include "port-sockets.h"
int
accept_a_connection (int debug_port, struct sockaddr *from,
socklen_t *fromlenp)
{
int n, s, fd, s4 = -1, s6 = -1, on = 1;
fd_set sockets;
FD_ZERO(&sockets);
#ifdef KRB5_USE_INET6
{
struct sockaddr_in6 sock_in6;
if ((s = socket(AF_INET6, SOCK_STREAM, PF_UNSPEC)) < 0) {
if ((errno == EPROTONOSUPPORT) || (errno == EAFNOSUPPORT))
goto skip_ipv6;
fprintf(stderr, "Error in socket(INET6): %s\n", strerror(errno));
exit(2);
}
memset((char *) &sock_in6, 0,sizeof(sock_in6));
sock_in6.sin6_family = AF_INET6;
sock_in6.sin6_port = htons(debug_port);
sock_in6.sin6_addr = in6addr_any;
(void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
(char *)&on, sizeof(on));
if ((bind(s, (struct sockaddr *) &sock_in6, sizeof(sock_in6))) < 0) {
fprintf(stderr, "Error in bind(INET6): %s\n", strerror(errno));
exit(2);
}
if ((listen(s, 5)) < 0) {
fprintf(stderr, "Error in listen(INET6): %s\n", strerror(errno));
exit(2);
}
s6 = s;
FD_SET(s, &sockets);
skip_ipv6:
;
}
#endif
{
struct sockaddr_in sock_in;
if ((s = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) < 0) {
fprintf(stderr, "Error in socket: %s\n", strerror(errno));
exit(2);
}
memset((char *) &sock_in, 0,sizeof(sock_in));
sock_in.sin_family = AF_INET;
sock_in.sin_port = htons(debug_port);
sock_in.sin_addr.s_addr = INADDR_ANY;
(void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
(char *)&on, sizeof(on));
if ((bind(s, (struct sockaddr *) &sock_in, sizeof(sock_in))) < 0) {
if (s6 >= 0 && errno == EADDRINUSE)
goto try_ipv6_only;
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);
}
s4 = s;
FD_SET(s, &sockets);
try_ipv6_only:
;
}
if (s4 == -1 && s6 == -1) {
fprintf(stderr, "No valid sockets established, exiting\n");
exit(2);
}
n = select(((s4 < s6) ? s6 : s4) + 1, &sockets, 0, 0, 0);
if (n < 0) {
fprintf(stderr, "select error: %s\n", strerror(errno));
exit(2);
} else if (n == 0) {
fprintf(stderr, "internal error? select returns 0\n");
exit(2);
}
if (s6 != -1 && FD_ISSET(s6, &sockets)) {
if (s4 != -1)
close(s4);
s = s6;
} else if (FD_ISSET(s4, &sockets)) {
if (s6 != -1)
close(s6);
s = s4;
} else {
fprintf(stderr,
"internal error? select returns positive, "
"but neither fd available\n");
exit(2);
}
if ((fd = accept(s, from, fromlenp)) < 0) {
fprintf(stderr, "Error in accept: %s\n", strerror(errno));
exit(2);
}
close(s);
return fd;
}