mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-02-10 00:37:00 +08:00
kkdcp: allow the handling of multiple realms per URL
This commit is contained in:
@@ -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
|
||||
|
||||
106
src/config.c
106
src/config.c
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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
|
||||
|
||||
13
src/vpn.h
13
src/vpn.h
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user