xref: /freebsd/crypto/krb5/src/lib/krad/client.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1*7f2fe78bSCy Schubert /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2*7f2fe78bSCy Schubert /* lib/krad/client.c - Client request code for libkrad */
3*7f2fe78bSCy Schubert /*
4*7f2fe78bSCy Schubert  * Copyright 2013 Red Hat, Inc.  All rights reserved.
5*7f2fe78bSCy Schubert  *
6*7f2fe78bSCy Schubert  * Redistribution and use in source and binary forms, with or without
7*7f2fe78bSCy Schubert  * modification, are permitted provided that the following conditions are met:
8*7f2fe78bSCy Schubert  *
9*7f2fe78bSCy Schubert  *    1. Redistributions of source code must retain the above copyright
10*7f2fe78bSCy Schubert  *       notice, this list of conditions and the following disclaimer.
11*7f2fe78bSCy Schubert  *
12*7f2fe78bSCy Schubert  *    2. Redistributions in binary form must reproduce the above copyright
13*7f2fe78bSCy Schubert  *       notice, this list of conditions and the following disclaimer in
14*7f2fe78bSCy Schubert  *       the documentation and/or other materials provided with the
15*7f2fe78bSCy Schubert  *       distribution.
16*7f2fe78bSCy Schubert  *
17*7f2fe78bSCy Schubert  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18*7f2fe78bSCy Schubert  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19*7f2fe78bSCy Schubert  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20*7f2fe78bSCy Schubert  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21*7f2fe78bSCy Schubert  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22*7f2fe78bSCy Schubert  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23*7f2fe78bSCy Schubert  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24*7f2fe78bSCy Schubert  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25*7f2fe78bSCy Schubert  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26*7f2fe78bSCy Schubert  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27*7f2fe78bSCy Schubert  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*7f2fe78bSCy Schubert  */
29*7f2fe78bSCy Schubert 
30*7f2fe78bSCy Schubert #include <k5-queue.h>
31*7f2fe78bSCy Schubert #include "internal.h"
32*7f2fe78bSCy Schubert 
33*7f2fe78bSCy Schubert #include <string.h>
34*7f2fe78bSCy Schubert #include <sys/un.h>
35*7f2fe78bSCy Schubert #include <unistd.h>
36*7f2fe78bSCy Schubert #include <stdio.h>
37*7f2fe78bSCy Schubert #include <limits.h>
38*7f2fe78bSCy Schubert 
39*7f2fe78bSCy Schubert K5_LIST_HEAD(server_head, server_st);
40*7f2fe78bSCy Schubert 
41*7f2fe78bSCy Schubert typedef struct remote_state_st remote_state;
42*7f2fe78bSCy Schubert typedef struct request_st request;
43*7f2fe78bSCy Schubert typedef struct server_st server;
44*7f2fe78bSCy Schubert 
45*7f2fe78bSCy Schubert struct remote_state_st {
46*7f2fe78bSCy Schubert     const krad_packet *packet;
47*7f2fe78bSCy Schubert     krad_remote *remote;
48*7f2fe78bSCy Schubert };
49*7f2fe78bSCy Schubert 
50*7f2fe78bSCy Schubert struct request_st {
51*7f2fe78bSCy Schubert     krad_client *rc;
52*7f2fe78bSCy Schubert 
53*7f2fe78bSCy Schubert     krad_code code;
54*7f2fe78bSCy Schubert     krad_attrset *attrs;
55*7f2fe78bSCy Schubert     int timeout;
56*7f2fe78bSCy Schubert     size_t retries;
57*7f2fe78bSCy Schubert     krad_cb cb;
58*7f2fe78bSCy Schubert     void *data;
59*7f2fe78bSCy Schubert 
60*7f2fe78bSCy Schubert     remote_state *remotes;
61*7f2fe78bSCy Schubert     ssize_t current;
62*7f2fe78bSCy Schubert     ssize_t count;
63*7f2fe78bSCy Schubert };
64*7f2fe78bSCy Schubert 
65*7f2fe78bSCy Schubert struct server_st {
66*7f2fe78bSCy Schubert     krad_remote *serv;
67*7f2fe78bSCy Schubert     K5_LIST_ENTRY(server_st) list;
68*7f2fe78bSCy Schubert };
69*7f2fe78bSCy Schubert 
70*7f2fe78bSCy Schubert struct krad_client_st {
71*7f2fe78bSCy Schubert     krb5_context kctx;
72*7f2fe78bSCy Schubert     verto_ctx *vctx;
73*7f2fe78bSCy Schubert     struct server_head servers;
74*7f2fe78bSCy Schubert };
75*7f2fe78bSCy Schubert 
76*7f2fe78bSCy Schubert /* Return either a pre-existing server that matches the address info and the
77*7f2fe78bSCy Schubert  * secret, or create a new one. */
78*7f2fe78bSCy Schubert static krb5_error_code
get_server(krad_client * rc,const struct addrinfo * ai,const char * secret,krad_remote ** out)79*7f2fe78bSCy Schubert get_server(krad_client *rc, const struct addrinfo *ai, const char *secret,
80*7f2fe78bSCy Schubert            krad_remote **out)
81*7f2fe78bSCy Schubert {
82*7f2fe78bSCy Schubert     krb5_error_code retval;
83*7f2fe78bSCy Schubert     server *srv;
84*7f2fe78bSCy Schubert 
85*7f2fe78bSCy Schubert     K5_LIST_FOREACH(srv, &rc->servers, list) {
86*7f2fe78bSCy Schubert         if (kr_remote_equals(srv->serv, ai, secret)) {
87*7f2fe78bSCy Schubert             *out = srv->serv;
88*7f2fe78bSCy Schubert             return 0;
89*7f2fe78bSCy Schubert         }
90*7f2fe78bSCy Schubert     }
91*7f2fe78bSCy Schubert 
92*7f2fe78bSCy Schubert     srv = calloc(1, sizeof(server));
93*7f2fe78bSCy Schubert     if (srv == NULL)
94*7f2fe78bSCy Schubert         return ENOMEM;
95*7f2fe78bSCy Schubert 
96*7f2fe78bSCy Schubert     retval = kr_remote_new(rc->kctx, rc->vctx, ai, secret, &srv->serv);
97*7f2fe78bSCy Schubert     if (retval != 0) {
98*7f2fe78bSCy Schubert         free(srv);
99*7f2fe78bSCy Schubert         return retval;
100*7f2fe78bSCy Schubert     }
101*7f2fe78bSCy Schubert 
102*7f2fe78bSCy Schubert     K5_LIST_INSERT_HEAD(&rc->servers, srv, list);
103*7f2fe78bSCy Schubert     *out = srv->serv;
104*7f2fe78bSCy Schubert     return 0;
105*7f2fe78bSCy Schubert }
106*7f2fe78bSCy Schubert 
107*7f2fe78bSCy Schubert /* Free a request. */
108*7f2fe78bSCy Schubert static void
request_free(request * req)109*7f2fe78bSCy Schubert request_free(request *req)
110*7f2fe78bSCy Schubert {
111*7f2fe78bSCy Schubert     krad_attrset_free(req->attrs);
112*7f2fe78bSCy Schubert     free(req->remotes);
113*7f2fe78bSCy Schubert     free(req);
114*7f2fe78bSCy Schubert }
115*7f2fe78bSCy Schubert 
116*7f2fe78bSCy Schubert /* Create a request. */
117*7f2fe78bSCy Schubert static krb5_error_code
request_new(krad_client * rc,krad_code code,const krad_attrset * attrs,const struct addrinfo * ai,const char * secret,int timeout,size_t retries,krad_cb cb,void * data,request ** req)118*7f2fe78bSCy Schubert request_new(krad_client *rc, krad_code code, const krad_attrset *attrs,
119*7f2fe78bSCy Schubert             const struct addrinfo *ai, const char *secret, int timeout,
120*7f2fe78bSCy Schubert             size_t retries, krad_cb cb, void *data, request **req)
121*7f2fe78bSCy Schubert {
122*7f2fe78bSCy Schubert     const struct addrinfo *tmp;
123*7f2fe78bSCy Schubert     krb5_error_code retval;
124*7f2fe78bSCy Schubert     request *rqst;
125*7f2fe78bSCy Schubert     size_t i;
126*7f2fe78bSCy Schubert 
127*7f2fe78bSCy Schubert     if (ai == NULL)
128*7f2fe78bSCy Schubert         return EINVAL;
129*7f2fe78bSCy Schubert 
130*7f2fe78bSCy Schubert     rqst = calloc(1, sizeof(request));
131*7f2fe78bSCy Schubert     if (rqst == NULL)
132*7f2fe78bSCy Schubert         return ENOMEM;
133*7f2fe78bSCy Schubert 
134*7f2fe78bSCy Schubert     for (tmp = ai; tmp != NULL; tmp = tmp->ai_next)
135*7f2fe78bSCy Schubert         rqst->count++;
136*7f2fe78bSCy Schubert 
137*7f2fe78bSCy Schubert     rqst->rc = rc;
138*7f2fe78bSCy Schubert     rqst->code = code;
139*7f2fe78bSCy Schubert     rqst->cb = cb;
140*7f2fe78bSCy Schubert     rqst->data = data;
141*7f2fe78bSCy Schubert     rqst->timeout = timeout / rqst->count;
142*7f2fe78bSCy Schubert     rqst->retries = retries;
143*7f2fe78bSCy Schubert 
144*7f2fe78bSCy Schubert     retval = krad_attrset_copy(attrs, &rqst->attrs);
145*7f2fe78bSCy Schubert     if (retval != 0) {
146*7f2fe78bSCy Schubert         request_free(rqst);
147*7f2fe78bSCy Schubert         return retval;
148*7f2fe78bSCy Schubert     }
149*7f2fe78bSCy Schubert 
150*7f2fe78bSCy Schubert     rqst->remotes = calloc(rqst->count + 1, sizeof(remote_state));
151*7f2fe78bSCy Schubert     if (rqst->remotes == NULL) {
152*7f2fe78bSCy Schubert         request_free(rqst);
153*7f2fe78bSCy Schubert         return ENOMEM;
154*7f2fe78bSCy Schubert     }
155*7f2fe78bSCy Schubert 
156*7f2fe78bSCy Schubert     i = 0;
157*7f2fe78bSCy Schubert     for (tmp = ai; tmp != NULL; tmp = tmp->ai_next) {
158*7f2fe78bSCy Schubert         retval = get_server(rc, tmp, secret, &rqst->remotes[i++].remote);
159*7f2fe78bSCy Schubert         if (retval != 0) {
160*7f2fe78bSCy Schubert             request_free(rqst);
161*7f2fe78bSCy Schubert             return retval;
162*7f2fe78bSCy Schubert         }
163*7f2fe78bSCy Schubert     }
164*7f2fe78bSCy Schubert 
165*7f2fe78bSCy Schubert     *req = rqst;
166*7f2fe78bSCy Schubert     return 0;
167*7f2fe78bSCy Schubert }
168*7f2fe78bSCy Schubert 
169*7f2fe78bSCy Schubert /* Handle a response from a server (or related errors). */
170*7f2fe78bSCy Schubert static void
on_response(krb5_error_code retval,const krad_packet * reqp,const krad_packet * rspp,void * data)171*7f2fe78bSCy Schubert on_response(krb5_error_code retval, const krad_packet *reqp,
172*7f2fe78bSCy Schubert             const krad_packet *rspp, void *data)
173*7f2fe78bSCy Schubert {
174*7f2fe78bSCy Schubert     request *req = data;
175*7f2fe78bSCy Schubert     size_t i;
176*7f2fe78bSCy Schubert 
177*7f2fe78bSCy Schubert     /* Do nothing if we are already completed. */
178*7f2fe78bSCy Schubert     if (req->count < 0)
179*7f2fe78bSCy Schubert         return;
180*7f2fe78bSCy Schubert 
181*7f2fe78bSCy Schubert     /* If we have timed out and have more remotes to try, do so. */
182*7f2fe78bSCy Schubert     if (retval == ETIMEDOUT && req->remotes[++req->current].remote != NULL) {
183*7f2fe78bSCy Schubert         retval = kr_remote_send(req->remotes[req->current].remote, req->code,
184*7f2fe78bSCy Schubert                                 req->attrs, on_response, req, req->timeout,
185*7f2fe78bSCy Schubert                                 req->retries,
186*7f2fe78bSCy Schubert                                 &req->remotes[req->current].packet);
187*7f2fe78bSCy Schubert         if (retval == 0)
188*7f2fe78bSCy Schubert             return;
189*7f2fe78bSCy Schubert     }
190*7f2fe78bSCy Schubert 
191*7f2fe78bSCy Schubert     /* Mark the request as complete. */
192*7f2fe78bSCy Schubert     req->count = -1;
193*7f2fe78bSCy Schubert 
194*7f2fe78bSCy Schubert     /* Inform the callback. */
195*7f2fe78bSCy Schubert     req->cb(retval, reqp, rspp, req->data);
196*7f2fe78bSCy Schubert 
197*7f2fe78bSCy Schubert     /* Cancel the outstanding packets. */
198*7f2fe78bSCy Schubert     for (i = 0; req->remotes[i].remote != NULL; i++)
199*7f2fe78bSCy Schubert         kr_remote_cancel(req->remotes[i].remote, req->remotes[i].packet);
200*7f2fe78bSCy Schubert 
201*7f2fe78bSCy Schubert     request_free(req);
202*7f2fe78bSCy Schubert }
203*7f2fe78bSCy Schubert 
204*7f2fe78bSCy Schubert krb5_error_code
krad_client_new(krb5_context kctx,verto_ctx * vctx,krad_client ** out)205*7f2fe78bSCy Schubert krad_client_new(krb5_context kctx, verto_ctx *vctx, krad_client **out)
206*7f2fe78bSCy Schubert {
207*7f2fe78bSCy Schubert     krad_client *tmp;
208*7f2fe78bSCy Schubert 
209*7f2fe78bSCy Schubert     tmp = calloc(1, sizeof(krad_client));
210*7f2fe78bSCy Schubert     if (tmp == NULL)
211*7f2fe78bSCy Schubert         return ENOMEM;
212*7f2fe78bSCy Schubert 
213*7f2fe78bSCy Schubert     tmp->kctx = kctx;
214*7f2fe78bSCy Schubert     tmp->vctx = vctx;
215*7f2fe78bSCy Schubert 
216*7f2fe78bSCy Schubert     *out = tmp;
217*7f2fe78bSCy Schubert     return 0;
218*7f2fe78bSCy Schubert }
219*7f2fe78bSCy Schubert 
220*7f2fe78bSCy Schubert void
krad_client_free(krad_client * rc)221*7f2fe78bSCy Schubert krad_client_free(krad_client *rc)
222*7f2fe78bSCy Schubert {
223*7f2fe78bSCy Schubert     server *srv;
224*7f2fe78bSCy Schubert 
225*7f2fe78bSCy Schubert     if (rc == NULL)
226*7f2fe78bSCy Schubert         return;
227*7f2fe78bSCy Schubert 
228*7f2fe78bSCy Schubert     /* Cancel all requests before freeing any remotes, since each request's
229*7f2fe78bSCy Schubert      * callback data may contain references to multiple remotes. */
230*7f2fe78bSCy Schubert     K5_LIST_FOREACH(srv, &rc->servers, list)
231*7f2fe78bSCy Schubert         kr_remote_cancel_all(srv->serv);
232*7f2fe78bSCy Schubert 
233*7f2fe78bSCy Schubert     while (!K5_LIST_EMPTY(&rc->servers)) {
234*7f2fe78bSCy Schubert         srv = K5_LIST_FIRST(&rc->servers);
235*7f2fe78bSCy Schubert         K5_LIST_REMOVE(srv, list);
236*7f2fe78bSCy Schubert         kr_remote_free(srv->serv);
237*7f2fe78bSCy Schubert         free(srv);
238*7f2fe78bSCy Schubert     }
239*7f2fe78bSCy Schubert 
240*7f2fe78bSCy Schubert     free(rc);
241*7f2fe78bSCy Schubert }
242*7f2fe78bSCy Schubert 
243*7f2fe78bSCy Schubert static krb5_error_code
resolve_remote(const char * remote,struct addrinfo ** ai)244*7f2fe78bSCy Schubert resolve_remote(const char *remote, struct addrinfo **ai)
245*7f2fe78bSCy Schubert {
246*7f2fe78bSCy Schubert     const char *svc = "radius";
247*7f2fe78bSCy Schubert     krb5_error_code retval;
248*7f2fe78bSCy Schubert     struct addrinfo hints;
249*7f2fe78bSCy Schubert     char *sep, *srv;
250*7f2fe78bSCy Schubert 
251*7f2fe78bSCy Schubert     /* Isolate the port number if it exists. */
252*7f2fe78bSCy Schubert     srv = strdup(remote);
253*7f2fe78bSCy Schubert     if (srv == NULL)
254*7f2fe78bSCy Schubert         return ENOMEM;
255*7f2fe78bSCy Schubert 
256*7f2fe78bSCy Schubert     if (srv[0] == '[') {
257*7f2fe78bSCy Schubert         /* IPv6 */
258*7f2fe78bSCy Schubert         sep = strrchr(srv, ']');
259*7f2fe78bSCy Schubert         if (sep != NULL && sep[1] == ':') {
260*7f2fe78bSCy Schubert             sep[1] = '\0';
261*7f2fe78bSCy Schubert             svc = &sep[2];
262*7f2fe78bSCy Schubert         }
263*7f2fe78bSCy Schubert     } else {
264*7f2fe78bSCy Schubert         /* IPv4 or DNS */
265*7f2fe78bSCy Schubert         sep = strrchr(srv, ':');
266*7f2fe78bSCy Schubert         if (sep != NULL && sep[1] != '\0') {
267*7f2fe78bSCy Schubert             sep[0] = '\0';
268*7f2fe78bSCy Schubert             svc = &sep[1];
269*7f2fe78bSCy Schubert         }
270*7f2fe78bSCy Schubert     }
271*7f2fe78bSCy Schubert 
272*7f2fe78bSCy Schubert     /* Perform the lookup. */
273*7f2fe78bSCy Schubert     memset(&hints, 0, sizeof(hints));
274*7f2fe78bSCy Schubert     hints.ai_socktype = SOCK_DGRAM;
275*7f2fe78bSCy Schubert     retval = gai_error_code(getaddrinfo(srv, svc, &hints, ai));
276*7f2fe78bSCy Schubert     free(srv);
277*7f2fe78bSCy Schubert     return retval;
278*7f2fe78bSCy Schubert }
279*7f2fe78bSCy Schubert 
280*7f2fe78bSCy Schubert krb5_error_code
krad_client_send(krad_client * rc,krad_code code,const krad_attrset * attrs,const char * remote,const char * secret,int timeout,size_t retries,krad_cb cb,void * data)281*7f2fe78bSCy Schubert krad_client_send(krad_client *rc, krad_code code, const krad_attrset *attrs,
282*7f2fe78bSCy Schubert                  const char *remote, const char *secret, int timeout,
283*7f2fe78bSCy Schubert                  size_t retries, krad_cb cb, void *data)
284*7f2fe78bSCy Schubert {
285*7f2fe78bSCy Schubert     struct addrinfo usock, *ai = NULL;
286*7f2fe78bSCy Schubert     krb5_error_code retval;
287*7f2fe78bSCy Schubert     struct sockaddr_un ua;
288*7f2fe78bSCy Schubert     request *req;
289*7f2fe78bSCy Schubert 
290*7f2fe78bSCy Schubert     if (remote[0] == '/') {
291*7f2fe78bSCy Schubert         ua.sun_family = AF_UNIX;
292*7f2fe78bSCy Schubert         snprintf(ua.sun_path, sizeof(ua.sun_path), "%s", remote);
293*7f2fe78bSCy Schubert         memset(&usock, 0, sizeof(usock));
294*7f2fe78bSCy Schubert         usock.ai_family = AF_UNIX;
295*7f2fe78bSCy Schubert         usock.ai_socktype = SOCK_STREAM;
296*7f2fe78bSCy Schubert         usock.ai_addr = (struct sockaddr *)&ua;
297*7f2fe78bSCy Schubert         usock.ai_addrlen = sizeof(ua);
298*7f2fe78bSCy Schubert 
299*7f2fe78bSCy Schubert         retval = request_new(rc, code, attrs, &usock, secret, timeout, retries,
300*7f2fe78bSCy Schubert                              cb, data, &req);
301*7f2fe78bSCy Schubert     } else {
302*7f2fe78bSCy Schubert         retval = resolve_remote(remote, &ai);
303*7f2fe78bSCy Schubert         if (retval == 0) {
304*7f2fe78bSCy Schubert             retval = request_new(rc, code, attrs, ai, secret, timeout, retries,
305*7f2fe78bSCy Schubert                                  cb, data, &req);
306*7f2fe78bSCy Schubert             freeaddrinfo(ai);
307*7f2fe78bSCy Schubert         }
308*7f2fe78bSCy Schubert     }
309*7f2fe78bSCy Schubert     if (retval != 0)
310*7f2fe78bSCy Schubert         return retval;
311*7f2fe78bSCy Schubert 
312*7f2fe78bSCy Schubert     retval = kr_remote_send(req->remotes[req->current].remote, req->code,
313*7f2fe78bSCy Schubert                             req->attrs, on_response, req, req->timeout,
314*7f2fe78bSCy Schubert                             req->retries, &req->remotes[req->current].packet);
315*7f2fe78bSCy Schubert     if (retval != 0) {
316*7f2fe78bSCy Schubert         request_free(req);
317*7f2fe78bSCy Schubert         return retval;
318*7f2fe78bSCy Schubert     }
319*7f2fe78bSCy Schubert 
320*7f2fe78bSCy Schubert     return 0;
321*7f2fe78bSCy Schubert }
322