kkdcp: allow the handling of multiple realms per URL

This commit is contained in:
Nikos Mavrogiannopoulos
2015-02-19 13:29:59 +01:00
parent 5f1f0ce87e
commit 9a3be087b4
7 changed files with 151 additions and 72 deletions

View File

@@ -416,9 +416,16 @@ no-route = 192.168.5.0/255.255.255.0
# This option allows you to specify a URL location where a client can
# post using MS-KKDCP, and the message will be forwarded to the provided
# KDC server. That is a translation URL between HTTP and Kerberos.
# In MIT kerberos you'll need to add in realms:
# EXAMPLE.COM = {
# kdc = https://ocserv.example.com/kerberos
# http_anchors = FILE:/etc/ocserv-ca.pem
# }
# This option is available if ocserv is compiled with GSSAPI support.
#kkdcp = /kerberos udp@127.0.0.1:88 application/kerberos
#kkdcp = /kerberos-tcp tcp@127.0.0.1:88 application/kerberos
#kkdcp = SERVER-PATH KERBEROS-REALM PROTOCOL@SERVER:PORT
#kkdcp = /kerberos EXAMPLE.COM udp@127.0.0.1:88
#kkdcp = /kerberos-tcp EXAMPLE.COM tcp@127.0.0.1:88
#
# The following options are for (experimental) AnyConnect client

View File

@@ -530,71 +530,102 @@ static void figure_auth_funcs(struct cfg_st *config, char **auth, unsigned auth_
#ifdef HAVE_GSSAPI
static void parse_kkdcp(struct cfg_st *config, char **urlfw, unsigned urlfw_size)
{
unsigned i;
char *p, *p2, *p3, *cont_type;
unsigned i, j;
char *path, *server, *port, *realm;
struct addrinfo hints, *res;
int ret;
struct kkdcp_st *kkdcp;
struct kkdcp_realm_st *kkdcp_realm;
config->kkdcp = talloc_size(config, urlfw_size*sizeof(kkdcp_st));
config->kkdcp = talloc_zero_size(config, urlfw_size*sizeof(kkdcp_st));
if (config->kkdcp == NULL) {
fprintf(stderr, "memory error\n");
exit(1);
}
config->kkdcp_size = 0;
for (i=0;i<urlfw_size;i++) {
p = urlfw[i];
p2 = strchr(p, ' ');
if (p2 == NULL) {
fprintf(stderr, "Cannot parse kkdcp string: %s\n", p);
path = urlfw[i];
realm = strchr(path, ' ');
if (realm == NULL) {
fprintf(stderr, "Cannot parse kkdcp string: %s\n", path);
exit(1);
}
*p2 = 0;
p2++;
*realm = 0;
realm++;
while (c_isspace(*realm))
realm++;
server = strchr(realm, ' ');
if (server == NULL) {
fprintf(stderr, "Cannot parse kkdcp string: %s\n", realm);
exit(1);
}
while (c_isspace(*server))
server++;
memset(&hints, 0, sizeof(hints));
if (strncmp(p2, "udp@", 4) == 0) {
if (strncmp(server, "udp@", 4) == 0) {
hints.ai_socktype = SOCK_DGRAM;
} else if (strncmp(p2, "tcp@", 4) == 0) {
} else if (strncmp(server, "tcp@", 4) == 0) {
hints.ai_socktype = SOCK_STREAM;
} else {
fprintf(stderr, "cannot handle protocol %s\n", p2);
fprintf(stderr, "cannot handle protocol %s\n", server);
exit(1);
}
p2 += 4;
server += 4;
cont_type = strchr(p2, ' ');
if (cont_type != NULL) {
*cont_type = 0;
cont_type++;
config->kkdcp[i].content_type = talloc_strdup(config->kkdcp, cont_type);
}
p3 = strchr(p2, ':');
if (p3 == NULL) {
fprintf(stderr, "No server port specified in: %s\n", p2);
port = strchr(server, ':');
if (port == NULL) {
fprintf(stderr, "No server port specified in: %s\n", server);
exit(1);
}
*p3 = 0;
p3++;
*port = 0;
port++;
ret = getaddrinfo(p2, p3, &hints, &res);
ret = getaddrinfo(server, port, &hints, &res);
if (ret != 0) {
fprintf(stderr, "getaddrinfo(%s) failed: %s\n", p2,
fprintf(stderr, "getaddrinfo(%s) failed: %s\n", server,
gai_strerror(ret));
exit(1);
}
memcpy(&config->kkdcp[i].addr, res->ai_addr, res->ai_addrlen);
config->kkdcp[i].addr_len = res->ai_addrlen;
config->kkdcp[i].ai_family = res->ai_family;
config->kkdcp[i].ai_socktype = res->ai_socktype;
config->kkdcp[i].ai_protocol = res->ai_protocol;
kkdcp = NULL;
/* check if the path is already added */
for (j=0;j<config->kkdcp_size;j++) {
if (strcmp(path, config->kkdcp[j].url) == 0) {
kkdcp = &config->kkdcp[j];
}
}
if (kkdcp == NULL) {
kkdcp = &config->kkdcp[i];
kkdcp->url = talloc_strdup(config->kkdcp, path);
config->kkdcp_size++;
}
if (kkdcp->realms_size >= MAX_KRB_REALMS) {
fprintf(stderr, "reached maximum number (%d) of realms per URL\n", MAX_KRB_REALMS);
exit(1);
}
kkdcp_realm = &kkdcp->realms[kkdcp->realms_size];
memcpy(&kkdcp_realm->addr, res->ai_addr, res->ai_addrlen);
kkdcp_realm->addr_len = res->ai_addrlen;
kkdcp_realm->ai_family = res->ai_family;
kkdcp_realm->ai_socktype = res->ai_socktype;
kkdcp_realm->ai_protocol = res->ai_protocol;
kkdcp_realm->realm = talloc_strdup(config->kkdcp, realm);
config->kkdcp[i].url = talloc_strdup(config->kkdcp, p);
freeaddrinfo(res);
kkdcp->realms_size++;
}
config->kkdcp_size = urlfw_size;
}
#endif
@@ -1081,8 +1112,13 @@ unsigned i;
DEL(config->proxy_url);
#ifdef HAVE_GSSAPI
for (i=0;i<config->kkdcp_size;i++)
for (i=0;i<config->kkdcp_size;i++) {
unsigned j;
DEL(config->kkdcp[i].url);
for (j=0;j<config->kkdcp[i].realms_size;j++) {
DEL(config->kkdcp[i].realms[j].realm);
}
}
DEL(config->kkdcp);
#endif

View File

@@ -6,7 +6,7 @@ BEGIN
KDC-PROXY-MESSAGE ::= SEQUENCE {
kerb-message [0] OCTET STRING,
target-domain [1] ANY OPTIONAL,
target-domain [1] IA5String OPTIONAL,
dclocator-hint [2] INTEGER OPTIONAL
}

View File

@@ -10,7 +10,7 @@ const asn1_static_node kkdcp_asn1_tab[] = {
{ "KDC-PROXY-MESSAGE", 536870917, NULL },
{ "kerb-message", 1610620935, NULL },
{ NULL, 2056, "0"},
{ "target-domain", 1610637325, NULL },
{ "target-domain", 1610637341, NULL },
{ NULL, 2056, "1"},
{ "dclocator-hint", 536895491, NULL },
{ NULL, 2056, "2"},

View File

@@ -496,9 +496,17 @@ no-route = 192.168.5.0/255.255.255.0
# This option allows you to specify a URL location where a client can
# post using MS-KKDCP, and the message will be forwarded to the provided
# KDC server. That is a translation URL between HTTP and Kerberos.
# You can have the same path used for multiple realms. To authenticate
# in client side, in MIT kerberos you'll need to add in krb5.conf:
# EXAMPLE.COM = {
# kdc = https://ocserv.example.com/kerberos
# http_anchors = FILE:/etc/ocserv-ca.pem
# }
# This option is available if ocserv is compiled with GSSAPI support.
#kkdcp = /kerberos udp@127.0.0.1:88 application/kerberos
#kkdcp = /kerberos-tcp tcp@127.0.0.1:88 application/kerberos
#kkdcp = SERVER-PATH KERBEROS-REALM PROTOCOL@SERVER:PORT
#kkdcp = /kerberos KERBEROS.REALM udp@127.0.0.1:88
#kkdcp = /kerberos-tcp KERBEROS.REALM tcp@127.0.0.1:88
#
# The following options are for (experimental) AnyConnect client

View File

@@ -208,6 +208,7 @@ struct vpn_st {
};
#define MAX_AUTH_METHODS 4
#define MAX_KRB_REALMS 16
typedef struct auth_struct_st {
const char *name;
@@ -217,14 +218,20 @@ typedef struct auth_struct_st {
bool enabled;
} auth_struct_st;
typedef struct kkdcp_st {
char *url;
char *content_type; /* Content-Type */
typedef struct kkdcp_realm_st {
char *realm;
struct sockaddr_storage addr;
socklen_t addr_len;
int ai_family;
int ai_socktype;
int ai_protocol;
} kkdcp_realm_st;
typedef struct kkdcp_st {
char *url;
/* the supported realms by this URL */
kkdcp_realm_st realms[MAX_KRB_REALMS];
unsigned realms_size;
} kkdcp_st;
struct cfg_st {

View File

@@ -30,7 +30,8 @@
#ifdef HAVE_GSSAPI
int der_decode(const uint8_t *der, unsigned der_size, uint8_t *out, unsigned *out_size, int *error)
int der_decode(const uint8_t *der, unsigned der_size, uint8_t *out, unsigned *out_size,
char *realm, unsigned realm_size, int *error)
{
int ret, len;
ASN1_TYPE c2 = ASN1_TYPE_EMPTY;
@@ -57,6 +58,13 @@ int der_decode(const uint8_t *der, unsigned der_size, uint8_t *out, unsigned *ou
}
*out_size = len;
len = realm_size;
ret = asn1_read_value(c2, "target-domain", realm, &len);
if (ret != ASN1_SUCCESS) {
/* no realm was given */
realm[0] = 0;
}
ret = 0;
cleanup:
asn1_delete_structure(&c2);
@@ -108,19 +116,21 @@ int post_kkdcp_handler(worker_st *ws, unsigned http_ver)
int ret, e, fd;
struct http_req_st *req = &ws->req;
unsigned i, length;
kkdcp_st *handler = NULL;
kkdcp_st *kkdcp = NULL;
uint8_t *buf = NULL;
uint32_t mlength;
char realm[128] = "";
const char *reason = "Unknown";
kkdcp_realm_st *kr;
for (i=0;i<ws->config->kkdcp_size;i++) {
if (ws->config->kkdcp[i].url && strcmp(ws->config->kkdcp[i].url, req->url) == 0) {
handler = &ws->config->kkdcp[i];
kkdcp = &ws->config->kkdcp[i];
break;
}
}
if (handler == NULL) {
if (kkdcp == NULL) {
oclog(ws, LOG_HTTP_DEBUG, "could not figure kkdcp handler for %s", req->url);
return -1;
}
@@ -132,19 +142,39 @@ int post_kkdcp_handler(worker_st *ws, unsigned http_ver)
oclog(ws, LOG_HTTP_DEBUG, "HTTP processing kkdcp framed request: %u bytes", (unsigned)req->body_length);
fd = socket(handler->ai_family, handler->ai_socktype, handler->ai_protocol);
ret = der_decode((uint8_t*)req->body, req->body_length, buf, &length, realm, sizeof(realm), &e);
if (ret < 0) {
oclog(ws, LOG_ERR, "kkdcp: DER decoding error: %s", asn1_strerror(e));
reason = "DER decoding error";
return -1;
}
kr = &kkdcp->realms[0];
if (realm[0] != 0 && kkdcp->realms_size > 1) {
oclog(ws, LOG_DEBUG, "kkdcp: client asked for '%s'", realm);
for (i=0;i<kkdcp->realms_size;i++) {
if (strcmp(kkdcp->realms[i].realm, realm) == 0) {
kr = &kkdcp->realms[i];
break;
}
}
}
fd = socket(kr->ai_family, kr->ai_socktype, kr->ai_protocol);
if (fd == -1) {
e = errno;
oclog(ws, LOG_ERR, "kkdcp: socket error: %s", strerror(e));
reason = "Socket error";
goto fail;
reason = "socket error";
return -1;
}
ret = connect(fd, (struct sockaddr*)&handler->addr, handler->addr_len);
ret = connect(fd, (struct sockaddr*)&kr->addr, kr->addr_len);
if (ret == -1) {
e = errno;
oclog(ws, LOG_ERR, "kkdcp: connect error: %s", strerror(e));
reason = "Connect error";
reason = "connect error";
goto fail;
}
@@ -152,14 +182,7 @@ int post_kkdcp_handler(worker_st *ws, unsigned http_ver)
buf = talloc_size(ws, length);
if (buf == NULL) {
oclog(ws, LOG_ERR, "kkdcp: memory error");
reason = "Memory error";
goto fail;
}
ret = der_decode((uint8_t*)req->body, req->body_length, buf, &length, &e);
if (ret < 0) {
oclog(ws, LOG_ERR, "kkdcp: DER decoding error: %s", asn1_strerror(e));
reason = "DER decoding error";
reason = "memory error";
goto fail;
}
@@ -172,16 +195,16 @@ int post_kkdcp_handler(worker_st *ws, unsigned http_ver)
} else {
oclog(ws, LOG_ERR, "kkdcp: send error: only %d were sent", ret);
}
reason = "Send error";
reason = "send error";
goto fail;
}
if (handler->ai_socktype == SOCK_DGRAM) {
if (kr->ai_socktype == SOCK_DGRAM) {
ret = recv(fd, buf, BUF_SIZE, 0);
if (ret == -1) {
e = errno;
oclog(ws, LOG_ERR, "kkdcp: recv error: %s", strerror(e));
reason = "Recv error";
reason = "recv error";
goto fail;
}
@@ -200,7 +223,7 @@ int post_kkdcp_handler(worker_st *ws, unsigned http_ver)
mlength = ntohl(mlength);
if (mlength >= BUF_SIZE-4) {
oclog(ws, LOG_ERR, "kkdcp: too long message (%d bytes)", (int)mlength);
reason = "Recv error";
reason = "recv error";
ret = -1;
goto fail;
}
@@ -209,7 +232,7 @@ int post_kkdcp_handler(worker_st *ws, unsigned http_ver)
if (ret == -1) {
e = errno;
oclog(ws, LOG_ERR, "kkdcp: recv error: %s", strerror(e));
reason = "Recv error";
reason = "recv error";
goto fail;
}
length = ret + 4;
@@ -223,12 +246,10 @@ int post_kkdcp_handler(worker_st *ws, unsigned http_ver)
goto fail;
}
if (handler->content_type) {
ret =
cstp_printf(ws, "Content-Type: %s\r\n", handler->content_type);
if (ret < 0) {
goto fail;
}
ret =
cstp_puts(ws, "Content-Type: application/kerberos\r\n");
if (ret < 0) {
goto fail;
}
ret = der_encode_inplace(buf, &length, BUF_SIZE, &e);