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