Path: blob/main/crypto/krb5/src/lib/krad/client.c
105585 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/* lib/krad/client.c - Client request code for libkrad */2/*3* Copyright 2013 Red Hat, Inc. All rights reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions are met:7*8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10*11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in13* the documentation and/or other materials provided with the14* distribution.15*16* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS17* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED18* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A19* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER20* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,21* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,22* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR23* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF24* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING25* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS26* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.27*/2829#include <k5-queue.h>30#include "internal.h"3132#include <string.h>33#include <sys/un.h>34#include <unistd.h>35#include <stdio.h>36#include <limits.h>3738K5_LIST_HEAD(server_head, server_st);3940typedef struct remote_state_st remote_state;41typedef struct request_st request;42typedef struct server_st server;4344struct remote_state_st {45const krad_packet *packet;46krad_remote *remote;47};4849struct request_st {50krad_client *rc;5152krad_code code;53krad_attrset *attrs;54int timeout;55size_t retries;56krad_cb cb;57void *data;5859remote_state *remotes;60ssize_t current;61ssize_t count;62};6364struct server_st {65krad_remote *serv;66K5_LIST_ENTRY(server_st) list;67};6869struct krad_client_st {70krb5_context kctx;71verto_ctx *vctx;72struct server_head servers;73};7475/* Return either a pre-existing server that matches the address info and the76* secret, or create a new one. */77static krb5_error_code78get_server(krad_client *rc, const struct addrinfo *ai, const char *secret,79krad_remote **out)80{81krb5_error_code retval;82server *srv;8384K5_LIST_FOREACH(srv, &rc->servers, list) {85if (kr_remote_equals(srv->serv, ai, secret)) {86*out = srv->serv;87return 0;88}89}9091srv = calloc(1, sizeof(server));92if (srv == NULL)93return ENOMEM;9495retval = kr_remote_new(rc->kctx, rc->vctx, ai, secret, &srv->serv);96if (retval != 0) {97free(srv);98return retval;99}100101K5_LIST_INSERT_HEAD(&rc->servers, srv, list);102*out = srv->serv;103return 0;104}105106/* Free a request. */107static void108request_free(request *req)109{110krad_attrset_free(req->attrs);111free(req->remotes);112free(req);113}114115/* Create a request. */116static krb5_error_code117request_new(krad_client *rc, krad_code code, const krad_attrset *attrs,118const struct addrinfo *ai, const char *secret, int timeout,119size_t retries, krad_cb cb, void *data, request **req)120{121const struct addrinfo *tmp;122krb5_error_code retval;123request *rqst;124size_t i;125126if (ai == NULL)127return EINVAL;128129rqst = calloc(1, sizeof(request));130if (rqst == NULL)131return ENOMEM;132133for (tmp = ai; tmp != NULL; tmp = tmp->ai_next)134rqst->count++;135136rqst->rc = rc;137rqst->code = code;138rqst->cb = cb;139rqst->data = data;140rqst->timeout = timeout / rqst->count;141rqst->retries = retries;142143retval = krad_attrset_copy(attrs, &rqst->attrs);144if (retval != 0) {145request_free(rqst);146return retval;147}148149rqst->remotes = calloc(rqst->count + 1, sizeof(remote_state));150if (rqst->remotes == NULL) {151request_free(rqst);152return ENOMEM;153}154155i = 0;156for (tmp = ai; tmp != NULL; tmp = tmp->ai_next) {157retval = get_server(rc, tmp, secret, &rqst->remotes[i++].remote);158if (retval != 0) {159request_free(rqst);160return retval;161}162}163164*req = rqst;165return 0;166}167168/* Handle a response from a server (or related errors). */169static void170on_response(krb5_error_code retval, const krad_packet *reqp,171const krad_packet *rspp, void *data)172{173request *req = data;174size_t i;175176/* Do nothing if we are already completed. */177if (req->count < 0)178return;179180/* If we have timed out and have more remotes to try, do so. */181if (retval == ETIMEDOUT && req->remotes[++req->current].remote != NULL) {182retval = kr_remote_send(req->remotes[req->current].remote, req->code,183req->attrs, on_response, req, req->timeout,184req->retries,185&req->remotes[req->current].packet);186if (retval == 0)187return;188}189190/* Mark the request as complete. */191req->count = -1;192193/* Inform the callback. */194req->cb(retval, reqp, rspp, req->data);195196/* Cancel the outstanding packets. */197for (i = 0; req->remotes[i].remote != NULL; i++)198kr_remote_cancel(req->remotes[i].remote, req->remotes[i].packet);199200request_free(req);201}202203krb5_error_code204krad_client_new(krb5_context kctx, verto_ctx *vctx, krad_client **out)205{206krad_client *tmp;207208tmp = calloc(1, sizeof(krad_client));209if (tmp == NULL)210return ENOMEM;211212tmp->kctx = kctx;213tmp->vctx = vctx;214215*out = tmp;216return 0;217}218219void220krad_client_free(krad_client *rc)221{222server *srv;223224if (rc == NULL)225return;226227/* Cancel all requests before freeing any remotes, since each request's228* callback data may contain references to multiple remotes. */229K5_LIST_FOREACH(srv, &rc->servers, list)230kr_remote_cancel_all(srv->serv);231232while (!K5_LIST_EMPTY(&rc->servers)) {233srv = K5_LIST_FIRST(&rc->servers);234K5_LIST_REMOVE(srv, list);235kr_remote_free(srv->serv);236free(srv);237}238239free(rc);240}241242static krb5_error_code243resolve_remote(const char *remote, struct addrinfo **ai)244{245const char *svc = "radius";246krb5_error_code retval;247struct addrinfo hints;248char *sep, *srv;249250/* Isolate the port number if it exists. */251srv = strdup(remote);252if (srv == NULL)253return ENOMEM;254255if (srv[0] == '[') {256/* IPv6 */257sep = strrchr(srv, ']');258if (sep != NULL && sep[1] == ':') {259sep[1] = '\0';260svc = &sep[2];261}262} else {263/* IPv4 or DNS */264sep = strrchr(srv, ':');265if (sep != NULL && sep[1] != '\0') {266sep[0] = '\0';267svc = &sep[1];268}269}270271/* Perform the lookup. */272memset(&hints, 0, sizeof(hints));273hints.ai_socktype = SOCK_DGRAM;274retval = gai_error_code(getaddrinfo(srv, svc, &hints, ai));275free(srv);276return retval;277}278279krb5_error_code280krad_client_send(krad_client *rc, krad_code code, const krad_attrset *attrs,281const char *remote, const char *secret, int timeout,282size_t retries, krad_cb cb, void *data)283{284struct addrinfo usock, *ai = NULL;285krb5_error_code retval;286struct sockaddr_un ua;287request *req;288289if (remote[0] == '/') {290ua.sun_family = AF_UNIX;291snprintf(ua.sun_path, sizeof(ua.sun_path), "%s", remote);292memset(&usock, 0, sizeof(usock));293usock.ai_family = AF_UNIX;294usock.ai_socktype = SOCK_STREAM;295usock.ai_addr = (struct sockaddr *)&ua;296usock.ai_addrlen = sizeof(ua);297298retval = request_new(rc, code, attrs, &usock, secret, timeout, retries,299cb, data, &req);300} else {301retval = resolve_remote(remote, &ai);302if (retval == 0) {303retval = request_new(rc, code, attrs, ai, secret, timeout, retries,304cb, data, &req);305freeaddrinfo(ai);306}307}308if (retval != 0)309return retval;310311retval = kr_remote_send(req->remotes[req->current].remote, req->code,312req->attrs, on_response, req, req->timeout,313req->retries, &req->remotes[req->current].packet);314if (retval != 0) {315request_free(req);316return retval;317}318319return 0;320}321322323