2 * util.c Various utility functions.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Copyright 2000 The FreeRADIUS server project
23 static const char rcsid[] = "$Id$";
26 #include "libradius.h"
41 #ifdef HAVE_NETINET_IN_H
42 #include <netinet/in.h>
46 #include "rad_assert.h"
49 * The signal() function in Solaris 2.5.1 sets SA_NODEFER in
50 * sa_flags, which causes grief if signal() is called in the
51 * handler before the cause of the signal has been cleared.
52 * (Infinite recursion).
54 * The same problem appears on HPUX, so we avoid it, if we can.
56 * Using sigaction() to reset the signal handler fixes the problem,
57 * so where available, we prefer that solution.
59 void (*reset_signal(int signo, void (*func)(int)))(int)
62 struct sigaction act, oact;
64 act.sa_handler = func;
65 sigemptyset(&act.sa_mask);
67 #ifdef SA_INTERRUPT /* SunOS */
68 act.sa_flags |= SA_INTERRUPT;
70 if (sigaction(signo, &act, &oact) < 0)
72 return oact.sa_handler;
76 * re-set by calling the 'signal' function, which
77 * may cause infinite recursion and core dumps due to
80 * However, the system is too dumb to implement sigaction(),
81 * so we don't have a choice.
88 * Per-request data, added by modules...
90 struct request_data_t {
96 void (*free_opaque)(void *);
100 * Add opaque data (with a "free" function) to a REQUEST.
102 * The unique ptr is meant to be a malloc'd module configuration,
103 * and the unique integer allows the caller to have multiple
104 * opaque data associated with a REQUEST.
106 int request_data_add(REQUEST *request,
107 void *unique_ptr, int unique_int,
108 void *opaque, void (*free_opaque)(void *))
110 request_data_t *this, **last, *next;
113 * Some simple sanity checks.
115 if (!request || !opaque) return -1;
118 for (last = &(request->data); *last != NULL; last = &((*last)->next)) {
119 if (((*last)->unique_ptr == unique_ptr) &&
120 ((*last)->unique_int == unique_int)) {
125 if (this->opaque && /* free it, if necessary */
127 this->free_opaque(this->opaque);
128 break; /* replace the existing entry */
132 if (!this) this = rad_malloc(sizeof(*this));
133 memset(this, 0, sizeof(*this));
136 this->unique_ptr = unique_ptr;
137 this->unique_int = unique_int;
138 this->opaque = opaque;
139 this->free_opaque = free_opaque;
148 * Get opaque data from a request.
150 void *request_data_get(REQUEST *request,
151 void *unique_ptr, int unique_int)
153 request_data_t **last;
155 for (last = &(request->data); *last != NULL; last = &((*last)->next)) {
156 if (((*last)->unique_ptr == unique_ptr) &&
157 ((*last)->unique_int == unique_int)) {
158 request_data_t *this = *last;
159 void *ptr = this->opaque;
162 * Remove the entry from the list, and free it.
166 return ptr; /* don't free it, the caller does that */
170 return NULL; /* wasn't found, too bad... */
175 * Free a REQUEST struct.
177 void request_free(REQUEST **request_ptr)
181 if (request_ptr == NULL)
184 request = *request_ptr;
187 * If there's a thread currently active on this request,
190 rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
193 rad_free(&request->packet);
196 rad_free(&request->proxy);
199 rad_free(&request->reply);
201 if (request->proxy_reply)
202 rad_free(&request->proxy_reply);
204 if (request->config_items)
205 pairfree(&request->config_items);
207 request->username = NULL;
208 request->password = NULL;
211 request_data_t *this, *next;
213 for (this = request->data; this != NULL; this = next) {
215 if (this->opaque && /* free it, if necessary */
217 this->free_opaque(this->opaque);
220 request->data = NULL;
224 request->magic = 0x01020304; /* set the request to be nonsense */
225 strcpy(request->secret, "REQUEST-DELETED");
226 strcpy(request->proxysecret, "REQUEST-DELETED");
234 * Check a filename for sanity.
236 * Allow only uppercase/lowercase letters, numbers, and '-_/.'
238 int rad_checkfilename(const char *filename)
240 if (strspn(filename, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_/.") == strlen(filename)) {
248 * Create possibly many directories.
250 * Note that the input directory name is NOT a constant!
251 * This is so that IF an error is returned, the 'directory' ptr
252 * points to the name of the file which caused the error.
254 int rad_mkdir(char *directory, int mode)
261 * If the directory exists, don't do anything.
263 if (stat(directory, &st) == 0) {
268 * Look for the LAST directory name. Try to create that,
269 * failing on any error.
271 p = strrchr(directory, '/');
274 rcode = rad_mkdir(directory, mode);
277 * On error, we leave the directory name as the
278 * one which caused the error.
285 * Reset the directory delimiter, and go ask
286 * the system to make the directory.
294 * Having done everything successfully, we do the
295 * system call to actually go create the directory.
297 return mkdir(directory, mode);
302 * Module malloc() call, which does stuff if the malloc fails.
304 * This call ALWAYS succeeds!
306 void *rad_malloc(size_t size)
308 void *ptr = malloc(size);
311 radlog(L_ERR|L_CONS, "no memory");
318 void xfree(const char *ptr)
324 * Logs an error message and aborts the program
328 void rad_assert_fail (const char *file, unsigned int line)
330 radlog(L_ERR|L_CONS, "Assertion failed in %s, line %u", file, line);
336 * Create a new REQUEST data structure.
338 REQUEST *request_alloc(void)
342 request = rad_malloc(sizeof(REQUEST));
343 memset(request, 0, sizeof(REQUEST));
345 request->magic = REQUEST_MAGIC;
347 request->proxy = NULL;
348 request->reply = NULL;
349 request->proxy_reply = NULL;
350 request->config_items = NULL;
351 request->username = NULL;
352 request->password = NULL;
353 request->timestamp = time(NULL);
354 request->child_pid = NO_SUCH_CHILD_PID;
355 request->container = NULL;
356 request->options = RAD_REQUEST_OPTION_NONE;
363 * Create a new REQUEST, based on an old one.
365 * This function allows modules to inject fake requests
366 * into the server, for tunneled protocols like TTLS & PEAP.
368 REQUEST *request_alloc_fake(REQUEST *oldreq)
372 request = request_alloc();
374 request->number = oldreq->number;
375 request->child_pid = NO_SUCH_CHILD_PID;
376 request->options = RAD_REQUEST_OPTION_FAKE_REQUEST;
378 request->packet = rad_alloc(0);
379 rad_assert(request->packet != NULL);
381 request->reply = rad_alloc(0);
382 rad_assert(request->reply != NULL);
385 * Fill in the fake request packet.
387 request->packet->sockfd = -1;
388 request->packet->src_ipaddr = htonl(INADDR_LOOPBACK);
389 request->packet->dst_ipaddr = htonl(INADDR_LOOPBACK);
390 request->packet->src_port = request->number >> 8;
391 request->packet->dst_port = 0;
394 * This isn't STRICTLY required, as the fake request SHOULD NEVER
395 * be put into the request list. However, it's still reasonable
398 request->packet->id = request->number & 0xff;
399 request->packet->code = oldreq->packet->code;
400 request->timestamp = oldreq->timestamp;
403 * Fill in the fake reply, based on the fake request.
405 request->reply->sockfd = request->packet->sockfd;
406 request->reply->dst_ipaddr = request->packet->src_ipaddr;
407 request->reply->dst_port = request->packet->src_port;
408 request->reply->id = request->packet->id;
409 request->reply->code = 0; /* UNKNOWN code */
416 * Perform any RFC specified cleaning of outgoing replies
418 void rfc_clean(RADIUS_PACKET *packet)
420 VALUE_PAIR *vps = NULL;
422 switch (packet->code) {
424 * In the default case, we just move all of the
433 * Accounting responses can only contain
434 * Proxy-State and VSA's. Note that we do NOT
435 * move the Proxy-State attributes over, as the
436 * Proxy-State attributes in this packet are NOT
437 * the right ones to use. The reply function
438 * takes care of copying those attributes from
439 * the original request, which ARE the right ones
442 case PW_ACCOUNTING_RESPONSE:
443 pairmove2(&vps, &(packet->vps), PW_VENDOR_SPECIFIC);
447 * Authentication REJECT's can have only
448 * EAP-Message, Message-Authenticator
449 * Reply-Message and Proxy-State.
451 * We delete everything other than these.
452 * Proxy-State is added below, just before the
455 case PW_AUTHENTICATION_REJECT:
456 pairmove2(&vps, &(packet->vps), PW_EAP_MESSAGE);
457 pairmove2(&vps, &(packet->vps), PW_MESSAGE_AUTHENTICATOR);
458 pairmove2(&vps, &(packet->vps), PW_REPLY_MESSAGE);
459 pairmove2(&vps, &(packet->vps), PW_VENDOR_SPECIFIC);
464 * Move the newly cleaned attributes over.
466 pairfree(&packet->vps);
470 * FIXME: Perform other, more generic sanity checks.
476 * Reject a request, by sending a trivial reply packet.
478 void request_reject(REQUEST *request)
483 * Already rejected. Don't do anything.
485 if (request->options & RAD_REQUEST_OPTION_REJECTED) {
489 DEBUG2("Server rejecting request %d.", request->number);
490 switch (request->packet->code) {
492 * Accounting requests, etc. get dropped on the floor.
495 case PW_ACCOUNTING_REQUEST:
496 case PW_STATUS_SERVER:
500 * Authentication requests get their Proxy-State
501 * attributes copied over, and an otherwise blank
502 * reject message sent.
504 case PW_AUTHENTICATION_REQUEST:
505 request->reply->code = PW_AUTHENTICATION_REJECT;
508 * Perform RFC limitations on outgoing replies.
510 rfc_clean(request->reply);
513 * Need to copy Proxy-State from request->packet->vps
515 vps = paircopy2(request->packet->vps, PW_PROXY_STATE);
517 pairadd(&(request->reply->vps), vps);
522 * If a reply exists, send it.
524 if (request->reply->code != 0) {
526 * If we're not delaying authentication rejects,
527 * then send the response immediately. Otherwise,
528 * mark the request as delayed, and do NOT send a
531 if (mainconfig.reject_delay == 0) {
532 rad_send(request->reply, request->packet,
535 request->options |= RAD_REQUEST_OPTION_DELAYED_REJECT;
540 * Remember that it was rejected.
542 request->options |= RAD_REQUEST_OPTION_REJECTED;