summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimo Sorce <ssorce@redhat.com>2008-05-28 16:24:46 -0400
committerSimo Sorce <ssorce@redhat.com>2008-05-29 09:50:45 -0400
commit739da8f2ca86c424be68e2cb88d6d2ec205be8fd (patch)
treedc51c484a439a09ceae307fcb70ecfa43d9a2ef9
parent55dd292ec091300211cfa913e86c32a8b394cd3e (diff)
downloadfreeipa-739da8f2ca86c424be68e2cb88d6d2ec205be8fd.tar.gz
freeipa-739da8f2ca86c424be68e2cb88d6d2ec205be8fd.tar.xz
freeipa-739da8f2ca86c424be68e2cb88d6d2ec205be8fd.zip
Make ipa_kpasswd listen on each single interface explicitly
instead of 0.0.0.0, this allows us to reply to a UDP request by using the right source address to build the kerberos reply.
-rw-r--r--ipa-server/ipa-kpasswd/ipa_kpasswd.c239
1 files changed, 173 insertions, 66 deletions
diff --git a/ipa-server/ipa-kpasswd/ipa_kpasswd.c b/ipa-server/ipa-kpasswd/ipa_kpasswd.c
index 8e83c515..c96d8a73 100644
--- a/ipa-server/ipa-kpasswd/ipa_kpasswd.c
+++ b/ipa-server/ipa-kpasswd/ipa_kpasswd.c
@@ -41,6 +41,7 @@
#include <krb5.h>
#include <ldap.h>
#include <sasl/sasl.h>
+#include <ifaddrs.h>
#define DEFAULT_KEYTAB "FILE:/var/kerberos/krb5kdc/kpasswd.keytab"
#define TMP_TEMPLATE "/var/cache/ipa/kpasswd/krb5_cc.XXXXXX"
@@ -61,6 +62,14 @@ struct blacklist {
static struct blacklist *global_blacklist = NULL;
+struct socklist {
+ int fd;
+ int socktype;
+ int dest_addr_len;
+ struct sockaddr_storage dest_addr;
+ struct socklist *next;
+};
+
int check_blacklist(char *address)
{
struct blacklist *bl, *prev_bl;
@@ -646,6 +655,7 @@ done:
}
void handle_krb_packets(uint8_t *buf, ssize_t buflen,
+ struct socklist *sd,
struct sockaddr_storage *from,
uint8_t **repbuf, ssize_t *replen)
{
@@ -654,7 +664,7 @@ void handle_krb_packets(uint8_t *buf, ssize_t buflen,
krb5_keytab keytab;
krb5_principal kprincpw;
krb5_ticket *ticket;
- krb5_address **lkaddr, rkaddr;
+ krb5_address lkaddr, rkaddr;
krb5_data kreq, krep, kenc, kdec;
krb5_replay_data replay;
krb5_error krb5err;
@@ -678,20 +688,31 @@ void handle_krb_packets(uint8_t *buf, ssize_t buflen,
kprincpw = NULL;
context = NULL;
ticket = NULL;
- lkaddr = NULL;
switch(((struct sockaddr *)from)->sa_family) {
case AF_INET:
+ lkaddr.addrtype = ADDRTYPE_INET;
+ lkaddr.length = sizeof(((struct sockaddr_in *)&sd->dest_addr)->sin_addr);
+ lkaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&sd->dest_addr)->sin_addr);
+
rkaddr.addrtype = ADDRTYPE_INET;
rkaddr.length = sizeof(((struct sockaddr_in *)from)->sin_addr);
rkaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)from)->sin_addr);
break;
case AF_INET6:
if (IN6_IS_ADDR_V4MAPPED (&((struct sockaddr_in6 *)from)->sin6_addr)) {
+ lkaddr.addrtype = ADDRTYPE_INET;
+ lkaddr.length = 4;
+ lkaddr.contents = 12 + (krb5_octet *) &(((struct sockaddr_in6 *)&sd->dest_addr)->sin6_addr);
+
rkaddr.addrtype = ADDRTYPE_INET;
rkaddr.length = 4;
rkaddr.contents = 12 + (krb5_octet *) &(((struct sockaddr_in6 *)from)->sin6_addr);
} else {
+ lkaddr.addrtype = ADDRTYPE_INET6;
+ lkaddr.length = sizeof(((struct sockaddr_in6 *)&sd->dest_addr)->sin6_addr);
+ lkaddr.contents = (krb5_octet *) &(((struct sockaddr_in6 *)&sd->dest_addr)->sin6_addr);
+
rkaddr.addrtype = ADDRTYPE_INET6;
rkaddr.length = sizeof(((struct sockaddr_in6 *)from)->sin6_addr);
rkaddr.contents = (krb5_octet *) &(((struct sockaddr_in6 *)from)->sin6_addr);
@@ -888,16 +909,7 @@ kpreply:
kdec.data[1] = result_err & 0xff;
memcpy(&kdec.data[2], result_string, strlen(result_string));
- /* we listen on ANYADDR, use this retrieve the right address */
- krberr = krb5_os_localaddr(context, &lkaddr);
- if (krberr) {
- result_string = strdup("Failed to retrieve local address");
- syslog(LOG_ERR, "%s: %s", result_string,
- krb5_get_error_message(context, krberr));
- goto done;
- }
-
- krberr = krb5_auth_con_setaddrs(context, auth_context, lkaddr[0], NULL);
+ krberr = krb5_auth_con_setaddrs(context, auth_context, &lkaddr, NULL);
if (krberr) {
result_string = strdup("Failed to set local address");
syslog(LOG_ERR, "%s: %s", result_string,
@@ -971,11 +983,10 @@ done:
if (krep.length) free(krep.data);
if (ticket) krb5_free_ticket(context, ticket);
if (kdec.length) free(kdec.data);
- if (lkaddr) krb5_free_addresses(context, lkaddr);
if (context) krb5_free_context(context);
}
-pid_t handle_conn(int fd, int type)
+pid_t handle_conn(struct socklist *sd)
{
int mfd, tcp;
pid_t pid;
@@ -988,14 +999,15 @@ pid_t handle_conn(int fd, int type)
struct sockaddr_storage from;
socklen_t fromlen;
ssize_t sendret;
+ int ret;
fromlen = sizeof(from);
tcp = 0;
/* receive request */
- if (type == SOCK_STREAM) {
+ if (sd->socktype == SOCK_STREAM) {
tcp = 1;
- mfd = accept(fd, (struct sockaddr *)&from, &fromlen);
+ mfd = accept(sd->fd, (struct sockaddr *)&from, &fromlen);
if (mfd == -1) {
syslog(LOG_ERR, "Accept failed with error (%d) %s",
errno, strerror(errno));
@@ -1003,7 +1015,7 @@ pid_t handle_conn(int fd, int type)
}
} else {
/* read first to empty the buffer on udp connections */
- reqlen = recvfrom(fd, request, sizeof(request), 0,
+ reqlen = recvfrom(sd->fd, request, sizeof(request), 0,
(struct sockaddr *)&from, &fromlen);
if (reqlen <= 0) {
syslog(LOG_ERR, "Error receiving request (%d) %s",
@@ -1013,9 +1025,13 @@ pid_t handle_conn(int fd, int type)
}
- (void) getnameinfo((struct sockaddr *)&from, fromlen,
+ ret = getnameinfo((struct sockaddr *)&from, fromlen,
addrto6, INET6_ADDRSTRLEN+1,
NULL, 0, NI_NUMERICHOST);
+ if (ret) {
+ syslog(LOG_ERR, "Error retrieving host address\n");
+ return -1;
+ }
if (debug > 0) {
syslog(LOG_ERR, "Connection from %s", addrto6);
@@ -1082,16 +1098,16 @@ pid_t handle_conn(int fd, int type)
* this information seem to be just redundant, so let's simply
* skip it */
if (tcp) {
- handle_krb_packets(request+4, reqlen-4, &from, &reply, &replen);
+ handle_krb_packets(request+4, reqlen-4, sd, &from, &reply, &replen);
} else {
- handle_krb_packets(request, reqlen, &from, &reply, &replen);
+ handle_krb_packets(request, reqlen, sd, &from, &reply, &replen);
}
if (replen) { /* we have something to reply */
if (tcp) {
sendret = sendto(mfd, reply, replen, 0, NULL, 0);
} else {
- sendret = sendto(fd, reply, replen, 0, (struct sockaddr *)&from, fromlen);
+ sendret = sendto(sd->fd, reply, replen, 0, (struct sockaddr *)&from, fromlen);
}
if (sendret == -1) {
syslog(LOG_ERR, "Error sending reply (%d)", errno);
@@ -1101,15 +1117,99 @@ pid_t handle_conn(int fd, int type)
exit(0);
}
-/* TODO: make this IPv6 aware */
+static int create_socket(struct addrinfo *ai, struct socklist **_sds,
+ struct pollfd **_pfds, int *_nfds)
+{
+ struct socklist *csd, *tsd;
+ struct pollfd *pfds;
+ int nfds;
+ int ret;
+ int tru = 1;
+
+ pfds = *_pfds;
+ nfds = *_nfds;
+
+ csd = calloc(1, sizeof(struct socklist));
+ if (csd == NULL) {
+ syslog(LOG_ERR, "Out of memory, can't create socklist\n");
+ return 1;
+ }
+ csd->socktype = ai->ai_socktype;
+ csd->dest_addr_len = ai->ai_addrlen;
+ memcpy(&csd->dest_addr, ai->ai_addr, ai->ai_addrlen);
+
+ csd->fd = socket(csd->dest_addr.ss_family, csd->socktype, 0);
+ if (csd->fd == -1) {
+ syslog(LOG_ERR, "Unable to create socket (%s)",
+ strerror(errno));
+ goto errout;
+ }
+ ret = setsockopt(csd->fd, SOL_SOCKET, SO_REUSEADDR,
+ (void *)&tru, sizeof(tru));
+
+ ret = bind(csd->fd, (struct sockaddr *)&csd->dest_addr, csd->dest_addr_len);
+ if (ret) {
+ if (errno != EADDRINUSE) {
+ syslog(LOG_ERR, "Unable to bind to socket");
+ close(csd->fd);
+ goto errout;
+ }
+ /* if EADDRINUSE it means we are on a machine
+ * with a dual ipv4/ipv6 stack that does not
+ * allow to bind on both at the same time as the
+ * ipv6 bind already allows connections on ipv4
+ * Just ignore */
+ close(csd->fd);
+ free(csd);
+ return 0;
+ }
+
+ if (csd->socktype == SOCK_STREAM) {
+ ret = listen(csd->fd, SOMAXCONN);
+ if (ret) {
+ syslog(LOG_ERR, "Unable to listen to TCP socket (%s)",
+ strerror(errno));
+ close(csd->fd);
+ goto errout;
+ }
+ }
+
+ pfds = realloc(pfds, sizeof(struct pollfd) * (nfds +1));
+ if (pfds == NULL) {
+ syslog(LOG_ERR, "Out of memory, can't alloc pollfd array\n");
+ close(csd->fd);
+ goto errout;
+ }
+ pfds[nfds].events = POLLIN;
+ pfds[nfds].fd = csd->fd;
+ nfds++;
+
+ if (*_sds) {
+ for (tsd = *_sds; tsd->next; tsd = tsd->next) /* skip */ ;
+ tsd->next = csd;
+ } else {
+ *_sds = csd;
+ }
+
+ *_pfds = pfds;
+ *_nfds = nfds;
+
+ return 0;
+
+errout:
+ free(csd);
+ return 1;
+}
int main(int argc, char *argv[])
{
pid_t pid;
+ struct ifaddrs *ifa, *tifa;
struct addrinfo *ai, *tai;
struct addrinfo hints;
- struct pollfd pfds[4];
- int pfdtype[4];
+ char host[NI_MAXHOST];
+ struct socklist *sds, *csd;
+ struct pollfd *pfds;
int nfds;
int ret;
char *env;
@@ -1167,13 +1267,9 @@ int main(int argc, char *argv[])
debug = strtol(env, NULL, 0);
}
- /* set hints */
- memset(&hints, 0, sizeof(hints));
- hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
-
- ret = getaddrinfo (NULL, "kpasswd", &hints, &ai);
+ ret = getifaddrs(&ifa);
if (ret) {
- syslog(LOG_ERR, "getaddrinfo failed: %s", gai_strerror(ret));
+ syslog(LOG_ERR, "getifaddrs failed: %s", gai_strerror(ret));
exit(1);
}
@@ -1187,54 +1283,61 @@ int main(int argc, char *argv[])
fail = 0;
}
if (fail) {
- syslog(LOG_ERR,"Couldn't create pid file %s: %s",
+ syslog(LOG_ERR, "Couldn't create pid file %s: %s",
pid_file, strerror(errno));
exit(1);
}
- tai = ai;
nfds = 0;
- /* we can have a maximum of 4 sockets (IPv4/IPv6(TCP/UDP)) */
- for (tai = ai; tai != NULL && nfds < 4; tai = tai->ai_next) {
- int tru = 1;
-
- pfds[nfds].fd = socket( tai->ai_family,
- tai->ai_socktype,
- tai->ai_protocol);
- if (pfds[nfds].fd == -1) {
- syslog(LOG_ERR, "Unable to create socket (%d)", nfds);
- exit(1);
+ pfds = NULL;
+ sds = NULL;
+
+ for (tifa = ifa; tifa; tifa = tifa->ifa_next) {
+
+ if (tifa->ifa_addr->sa_family != PF_INET &&
+ tifa->ifa_addr->sa_family != AF_INET6) {
+ /* not interesting for us */
+ continue;
+ }
+
+ ret = getnameinfo(tifa->ifa_addr, sizeof(struct sockaddr_storage),
+ host, sizeof(host), NULL, 0, NI_NUMERICHOST);
+ if (ret) {
+ syslog(LOG_ERR, "Error converting address (%d)",
+ gai_strerror(ret));
+ continue;
+ } else {
+ syslog(LOG_INFO, "Setting up socket for [%s]", host);
}
- pfds[nfds].events = POLLIN;
- ret = setsockopt(pfds[nfds].fd, SOL_SOCKET, SO_REUSEADDR,
- (void *)&tru, sizeof(tru));
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_NUMERICHOST;
+ hints.ai_family = AF_UNSPEC;
- ret = bind(pfds[nfds].fd, tai->ai_addr, tai->ai_addrlen);
+ /* this shoud return 2 entries, one for UDP and one for TCP */
+ ret = getaddrinfo(host, "kpasswd", &hints, &ai);
if (ret) {
- if (errno != EADDRINUSE) {
- syslog(LOG_ERR, "Unable to bind to socket");
- exit(1);
+ syslog(LOG_ERR, "Error getting address info (%d) for [%s]",
+ gai_strerror(ret), host);
+ continue;
+ }
+
+ for (tai = ai; tai; tai = tai->ai_next) {
+ char *socktype = (tai->ai_socktype==SOCK_STREAM)?"TCP":"UDP";
+ ret = create_socket(tai, &sds, &pfds, &nfds);
+ if (ret) {
+ syslog(LOG_ERR,
+ "Failed to set up %s socket for [%s]",
+ socktype, host);
}
- /* if EADDRINUSE it means we are on a machine
- * with a dual ipv4/ipv6 stack that does not
- * allow to bind on both at the same time as the
- * ipv6 bind already allows connections on ipv4
- * Just ignore */
- close(pfds[nfds].fd);
- } else {
- if (tai->ai_socktype == SOCK_STREAM) {
- ret = listen(pfds[nfds].fd, SOMAXCONN);
- if (ret) {
- syslog(LOG_ERR, "Unable to listen to socket");
- exit(1);
- }
- }
- pfdtype[nfds] = tai->ai_socktype;
- nfds++;
}
}
+ if (nfds == 0) {
+ syslog(LOG_ERR, "Failed to setup any socket. Aborting");
+ exit(1);
+ }
+
/* now that sockets are set up, enter the poll loop */
while (1) {
@@ -1256,7 +1359,11 @@ int main(int argc, char *argv[])
default:
for (i = 0; i < nfds; i++) {
if (pfds[i].revents & POLLIN) {
- handle_conn(pfds[i].fd, pfdtype[i]);
+ for (csd = sds; csd; csd = csd->next) {
+ if (csd->fd == pfds[i].fd) {
+ handle_conn(csd);
+ }
+ }
}
}
}