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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2000,2006 The FreeRADIUS server project
25 #include <freeradius-devel/radiusd.h>
26 #include <freeradius-devel/rad_assert.h>
34 * The signal() function in Solaris 2.5.1 sets SA_NODEFER in
35 * sa_flags, which causes grief if signal() is called in the
36 * handler before the cause of the signal has been cleared.
37 * (Infinite recursion).
39 * The same problem appears on HPUX, so we avoid it, if we can.
41 * Using sigaction() to reset the signal handler fixes the problem,
42 * so where available, we prefer that solution.
45 void (*reset_signal(int signo, void (*func)(int)))(int)
48 struct sigaction act, oact;
50 memset(&act, 0, sizeof(act));
51 act.sa_handler = func;
52 sigemptyset(&act.sa_mask);
54 #ifdef SA_INTERRUPT /* SunOS */
55 act.sa_flags |= SA_INTERRUPT;
57 if (sigaction(signo, &act, &oact) < 0)
59 return oact.sa_handler;
63 * re-set by calling the 'signal' function, which
64 * may cause infinite recursion and core dumps due to
67 * However, the system is too dumb to implement sigaction(),
68 * so we don't have a choice.
77 * Per-request data, added by modules...
79 struct request_data_t {
89 * Add opaque data (with a "free" function) to a REQUEST.
91 * The unique ptr is meant to be a module configuration,
92 * and the unique integer allows the caller to have multiple
93 * opaque data associated with a REQUEST.
95 int request_data_add(REQUEST *request, void *unique_ptr, int unique_int, void *opaque, bool free_opaque)
97 request_data_t *this, **last, *next;
100 * Some simple sanity checks.
102 if (!request || !opaque) return -1;
105 for (last = &(request->data);
107 last = &((*last)->next)) {
108 if (((*last)->unique_ptr == unique_ptr) &&
109 ((*last)->unique_int == unique_int)) {
114 * If caller requires custom behaviour on free
115 * they must set a destructor.
117 if (this->opaque && this->free_opaque) talloc_free(this->opaque);
119 break; /* replace the existing entry */
124 * Only alloc new memory if we're not replacing
127 if (!this) this = talloc_zero(request, request_data_t);
128 if (!this) return -1;
131 this->unique_ptr = unique_ptr;
132 this->unique_int = unique_int;
133 this->opaque = opaque;
134 this->free_opaque = free_opaque;
142 * Get opaque data from a request.
144 void *request_data_get(REQUEST *request, void *unique_ptr, int unique_int)
146 request_data_t **last;
148 if (!request) return NULL;
150 for (last = &(request->data);
152 last = &((*last)->next)) {
153 if (((*last)->unique_ptr == unique_ptr) &&
154 ((*last)->unique_int == unique_int)) {
155 request_data_t *this;
162 * Remove the entry from the list, and free it.
167 return ptr; /* don't free it, the caller does that */
171 return NULL; /* wasn't found, too bad... */
175 * Get opaque data from a request without removing it.
177 void *request_data_reference(REQUEST *request, void *unique_ptr, int unique_int)
179 request_data_t **last;
181 for (last = &(request->data);
183 last = &((*last)->next)) {
184 if (((*last)->unique_ptr == unique_ptr) &&
185 ((*last)->unique_int == unique_int)) {
186 return (*last)->opaque;
190 return NULL; /* wasn't found, too bad... */
194 * Create possibly many directories.
196 * Note that the input directory name is NOT a constant!
197 * This is so that IF an error is returned, the 'directory' ptr
198 * points to the name of the file which caused the error.
200 int rad_mkdir(char *directory, mode_t mode)
206 * Try to make the directory. If it exists, chmod it.
207 * If a path doesn't exist, that's OK. Otherwise
208 * return with an error.
210 rcode = mkdir(directory, mode & 0777);
212 if (errno == EEXIST) {
213 return 0; /* don't change permissions */
216 if (errno != ENOENT) {
221 * A component in the directory path doesn't
222 * exist. Look for the LAST directory name. Try
223 * to create that. If there's an error, we leave
224 * the directory path as the one at which the
227 p = strrchr(directory, FR_DIR_SEP);
228 if (!p || (p == directory)) return -1;
231 rcode = rad_mkdir(directory, mode);
232 if (rcode < 0) return rcode;
235 * Reset the directory path, and try again to
236 * make the directory.
239 rcode = mkdir(directory, mode & 0777);
240 if (rcode < 0) return rcode;
241 } /* else we successfully created the directory */
244 * Set the permissions on the created directory.
246 return chmod(directory, mode);
249 /** Escapes the raw string such that it should be safe to use as part of a file path
251 * This function is designed to produce a string that's still readable but portable
252 * across the majority of file systems.
254 * For security reasons it cannot remove characters from the name, and must not allow
255 * collisions to occur between different strings.
257 * With that in mind '-' has been chosen as the escape character, and will be double
258 * escaped '-' -> '--' to avoid collisions.
260 * Escaping should be reversible if the original string needs to be extracted.
262 * @note function takes additional arguments so that it may be used as an xlat escape
263 * function but it's fine to call it directly.
265 * @note OSX/Unix/NTFS/VFAT have a max filename size of 255 bytes.
267 * @param request Current request (may be NULL).
268 * @param out Output buffer.
269 * @param outlen Size of the output buffer.
270 * @param in string to escape.
271 * @param arg Context arguments (unused, should be NULL).
273 size_t rad_filename_escape(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
275 size_t freespace = outlen;
277 while (*in != '\0') {
281 * Encode multibyte UTF8 chars
283 utf8_len = fr_utf8_char((uint8_t const *) in);
285 if (freespace <= (utf8_len * 3)) break;
289 snprintf(out, freespace, "-%x-%x", in[0], in[1]);
293 snprintf(out, freespace, "-%x-%x-%x", in[0], in[1], in[2]);
297 snprintf(out, freespace, "-%x-%x-%x-%x", in[0], in[1], in[2], in[3]);
301 freespace -= (utf8_len * 3);
302 out += (utf8_len * 3);
311 if (((*in >= 'A') && (*in <= 'Z')) ||
312 ((*in >= 'a') && (*in <= 'z')) ||
313 ((*in >= '0') && (*in <= '9')) ||
314 (*in == '_') || (*in == '.')) {
315 if (freespace <= 1) break;
322 if (freespace <= 2) break;
325 * Double escape '-' (like \\)
340 fr_bin2hex(out, (uint8_t const *)in++, 1);
346 return outlen - freespace;
349 /** Converts data stored in a file name back to its original form
351 * @param out Where to write the unescaped string (may be the same as in).
352 * @param outlen Length of the output buffer.
353 * @param in Input filename.
354 * @param inlen Length of input.
355 * @return number of bytes written to output buffer, or offset where parse error
356 * occurred on failure.
358 ssize_t rad_filename_unescape(char *out, size_t outlen, char const *in, size_t inlen)
360 char const *p, *end = in + inlen;
361 size_t freespace = outlen;
363 for (p = in; p < end; p++) {
364 if (freespace <= 1) break;
366 if (((*p >= 'A') && (*p <= 'Z')) ||
367 ((*p >= 'a') && (*p <= 'z')) ||
368 ((*p >= '0') && (*p <= '9')) ||
369 (*p == '_') || (*p == '.')) {
377 * End of input, '-' needs at least one extra char after
380 if ((end - p) < 2) return in - p;
389 * End of input, '-' must be followed by <hex><hex>
390 * but there aren't enough chars left
392 if ((end - p) < 3) return in - p;
395 * If hex2bin returns 0 the next two chars weren't hexits.
397 if (fr_hex2bin((uint8_t *) out, 1, in, 1) == 0) return in - (p + 1);
403 return in - p; /* offset we found the bad char at */
407 return outlen - freespace; /* how many bytes were written */
411 * Allocate memory, or exit.
413 * This call ALWAYS succeeds!
415 void *rad_malloc(size_t size)
417 void *ptr = malloc(size);
428 void rad_const_free(void const *ptr)
433 memcpy(&tmp, &ptr, sizeof(tmp));
439 * Logs an error message and aborts the program
443 void NEVER_RETURNS rad_assert_fail(char const *file, unsigned int line, char const *expr)
445 ERROR("ASSERT FAILED %s[%u]: %s", file, line, expr);
451 * Free a REQUEST struct.
453 static int _request_free(REQUEST *request)
455 rad_assert(!request->in_request_hash);
457 rad_assert(!request->in_proxy_hash);
459 rad_assert(!request->ev);
463 request->coa->parent = NULL;
466 if (request->parent && (request->parent->coa == request)) {
467 request->parent->coa = NULL;
472 request->magic = 0x01020304; /* set the request to be nonsense */
474 request->client = NULL;
476 request->home_server = NULL;
483 * Create a new REQUEST data structure.
485 REQUEST *request_alloc(TALLOC_CTX *ctx)
489 request = talloc_zero(ctx, REQUEST);
490 talloc_set_destructor(request, _request_free);
492 request->magic = REQUEST_MAGIC;
495 request->proxy = NULL;
497 request->reply = NULL;
499 request->proxy_reply = NULL;
501 request->config_items = NULL;
502 request->username = NULL;
503 request->password = NULL;
504 request->timestamp = time(NULL);
505 request->log.lvl = debug_flag; /* Default to global debug level */
507 request->module = "";
508 request->component = "<core>";
509 request->log.func = vradlog_request;
516 * Create a new REQUEST, based on an old one.
518 * This function allows modules to inject fake requests
519 * into the server, for tunneled protocols like TTLS & PEAP.
521 REQUEST *request_alloc_fake(REQUEST *request)
525 fake = request_alloc(request);
527 fake->number = request->number;
528 #ifdef HAVE_PTHREAD_H
529 fake->child_pid = request->child_pid;
531 fake->parent = request;
532 fake->root = request->root;
533 fake->client = request->client;
536 * For new server support.
538 * FIXME: Key instead off of a "virtual server" data structure.
540 * FIXME: Permit different servers for inner && outer sessions?
542 fake->server = request->server;
544 fake->packet = rad_alloc(fake, true);
550 fake->reply = rad_alloc(fake, false);
556 fake->master_state = REQUEST_ACTIVE;
557 fake->child_state = REQUEST_RUNNING;
560 * Fill in the fake request.
562 fake->packet->sockfd = -1;
563 fake->packet->src_ipaddr = request->packet->src_ipaddr;
564 fake->packet->src_port = request->packet->src_port;
565 fake->packet->dst_ipaddr = request->packet->dst_ipaddr;
566 fake->packet->dst_port = 0;
569 * This isn't STRICTLY required, as the fake request MUST NEVER
570 * be put into the request list. However, it's still reasonable
573 fake->packet->id = fake->number & 0xff;
574 fake->packet->code = request->packet->code;
575 fake->timestamp = request->timestamp;
576 fake->packet->timestamp = request->packet->timestamp;
579 * Required for new identity support
581 fake->listener = request->listener;
584 * Fill in the fake reply, based on the fake request.
586 fake->reply->sockfd = fake->packet->sockfd;
587 fake->reply->src_ipaddr = fake->packet->dst_ipaddr;
588 fake->reply->src_port = fake->packet->dst_port;
589 fake->reply->dst_ipaddr = fake->packet->src_ipaddr;
590 fake->reply->dst_port = fake->packet->src_port;
591 fake->reply->id = fake->packet->id;
592 fake->reply->code = 0; /* UNKNOWN code */
595 * Copy debug information.
597 memcpy(&(fake->log), &(request->log), sizeof(fake->log));
603 REQUEST *request_alloc_coa(REQUEST *request)
605 if (!request || request->coa) return NULL;
608 * Originate CoA requests only when necessary.
610 if ((request->packet->code != PW_CODE_ACCESS_REQUEST) &&
611 (request->packet->code != PW_CODE_ACCOUNTING_REQUEST)) return NULL;
613 request->coa = request_alloc_fake(request);
614 if (!request->coa) return NULL;
616 request->coa->options = 1; /* is a CoA packet */
617 request->coa->packet->code = 0; /* unknown, as of yet */
618 request->coa->child_state = REQUEST_RUNNING;
619 request->coa->proxy = rad_alloc(request->coa, false);
620 if (!request->coa->proxy) {
621 TALLOC_FREE(request->coa);
630 * Copy a quoted string.
632 int rad_copy_string(char *to, char const *from)
644 } while (*from && (*from != quote));
646 if (*from != quote) return -1; /* not properly quoted */
656 * Copy a quoted string but without the quotes. The length
657 * returned is the number of chars written; the number of
658 * characters consumed is 2 more than this.
660 int rad_copy_string_bare(char *to, char const *from)
666 while (*from && (*from != quote)) {
675 if (*from != quote) return -1; /* not properly quoted */
686 int rad_copy_variable(char *to, char const *from)
698 sublen = rad_copy_string(to, from);
699 if (sublen < 0) return sublen;
705 case '}': /* end of variable expansion */
709 return length; /* proper end of variable */
717 case '%': /* start of variable expansion */
718 if (from[1] == '{') {
722 sublen = rad_copy_variable(to, from);
723 if (sublen < 0) return sublen;
728 } /* else FIXME: catch %%{ ?*/
736 } /* loop over the input string */
739 * We ended the string before a trailing '}'
749 uint32_t rad_pps(uint32_t *past, uint32_t *present, time_t *then, struct timeval *now)
753 if (*then != now->tv_sec) {
760 * Bootstrap PPS by looking at a percentage of
761 * the previous PPS. This lets us take a moving
762 * count, without doing a moving average. If
763 * we're a fraction "f" (0..1) into the current
764 * second, we can get a good guess for PPS by
767 * PPS = pps_now + pps_old * (1 - f)
769 * It's an instantaneous measurement, rather than
770 * a moving average. This will hopefully let it
771 * respond better to sudden spikes.
773 * Doing the calculations by thousands allows us
774 * to not overflow 2^32, AND to not underflow
775 * when we divide by USEC.
777 pps = USEC - now->tv_usec; /* useconds left in previous second */
778 pps /= 1000; /* scale to milliseconds */
779 pps *= *past; /* multiply by past count to get fraction */
780 pps /= 1000; /* scale to usec again */
781 pps += *present; /* add in current count */
786 /** Split string into words and expand each one
788 * @param request Current request.
789 * @param cmd string to split.
790 * @param max_argc the maximum number of arguments to split into.
791 * @param argv Where to write the pointers into argv_buf.
792 * @param can_fail If false, stop processing if any of the xlat expansions fail.
793 * @param argv_buflen size of argv_buf.
794 * @param argv_buf temporary buffer we used to mangle/expand cmd.
795 * Pointers to offsets of this buffer will be written to argv.
796 * @return argc or -1 on failure.
799 int rad_expand_xlat(REQUEST *request, char const *cmd,
800 int max_argc, char *argv[], bool can_fail,
801 size_t argv_buflen, char *argv_buf)
809 if (strlen(cmd) > (argv_buflen - 1)) {
810 ERROR("rad_expand_xlat: Command line is too long");
815 * Check for bad escapes.
817 if (cmd[strlen(cmd) - 1] == '\\') {
818 ERROR("rad_expand_xlat: Command line has final backslash, without a following character");
822 strlcpy(argv_buf, cmd, argv_buflen);
825 * Split the string into argv's BEFORE doing radius_xlat...
836 if ((*from == ' ') || (*from == '\t')) {
844 if (argc >= (max_argc - 1)) break;
847 * Copy the argv over to our buffer.
849 while (*from && (*from != ' ') && (*from != '\t')) {
850 if (to >= argv_buf + argv_buflen - 1) {
851 ERROR("rad_expand_xlat: Ran out of space in command line");
858 length = rad_copy_string_bare(to, from);
860 ERROR("rad_expand_xlat: Invalid string passed as argument");
868 if (from[1] == '{') {
871 length = rad_copy_variable(to, from);
873 ERROR("rad_expand_xlat: Invalid variable expansion passed as argument");
878 } else { /* FIXME: catch %%{ ? */
884 if (from[1] == ' ') from++;
890 } /* end of string, or found a space */
892 *(to++) = '\0'; /* terminate the string */
896 * We have to have SOMETHING, at least.
899 ERROR("rad_expand_xlat: Empty command line");
904 * Expand each string, as appropriate.
906 left = argv_buf + argv_buflen - to;
907 for (i = 0; i < argc; i++) {
911 * Don't touch argv's which won't be translated.
913 if (strchr(argv[i], '%') == NULL) continue;
915 if (!request) continue;
917 sublen = radius_xlat(to, left - 1, request, argv[i], NULL, NULL);
921 * Fail to be backwards compatible.
923 * It's yucky, but it won't break anything,
924 * and it won't cause security problems.
928 ERROR("rad_expand_xlat: xlat failed");
940 ERROR("rad_expand_xlat: Ran out of space while expanding arguments");
953 static void verify_packet(char const *file, int line, REQUEST *request, RADIUS_PACKET *packet, char const *type)
958 fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%i]: RADIUS_PACKET %s pointer was NULL", file, line, type);
963 parent = talloc_parent(packet);
964 if (parent != request) {
965 ERROR("CONSISTENCY CHECK FAILED %s[%i]: Expected RADIUS_PACKET %s to be parented by %p (%s), "
966 "but parented by %p (%s)", file, line, type, request, talloc_get_name(request),
967 parent, parent ? talloc_get_name(parent) : "NULL");
969 fr_log_talloc_report(packet);
970 if (parent) fr_log_talloc_report(parent);
975 VERIFY_PACKET(packet);
977 if (!packet->vps) return;
979 #ifdef WITH_VERIFY_PTR
980 fr_verify_list(file, line, packet, packet->vps);
984 * Catch horrible talloc errors.
986 void verify_request(char const *file, int line, REQUEST *request)
989 fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%i]: REQUEST pointer was NULL", file, line);
994 (void) talloc_get_type_abort(request, REQUEST);
996 #ifdef WITH_VERIFY_PTR
997 fr_verify_list(file, line, request, request->config_items);
998 fr_verify_list(file, line, request, request->state);
1001 if (request->packet) verify_packet(file, line, request, request->packet, "request");
1002 if (request->reply) verify_packet(file, line, request, request->reply, "reply");
1004 if (request->proxy) verify_packet(file, line, request, request->proxy, "proxy-request");
1005 if (request->proxy_reply) verify_packet(file, line, request, request->proxy_reply, "proxy-reply");
1012 (void) talloc_get_type_abort(request->coa, REQUEST);
1013 parent = talloc_parent(request->coa);
1015 rad_assert(parent == request);
1017 verify_request(file, line, request->coa);
1024 #ifndef HAVE_GETGRNAM_R
1025 bool fr_getgid(char const *name, gid_t *gid)
1029 grp = getgrnam(name);
1030 if (!grp) return false;
1036 #else /* getgrnam_r() exists */
1038 bool fr_getgid(char const *name, gid_t *gid)
1040 struct group *grp, my_group;
1042 size_t group_size = 1024;
1045 group_buffer = talloc_array(NULL, char, group_size);
1046 while (group_buffer) {
1049 err = getgrnam_r(name, &my_group, group_buffer, group_size, &grp);
1050 if (err == ERANGE) {
1052 group_buffer = talloc_realloc(NULL, group_buffer, char, group_size);
1056 if (err) errno = err; /* so the caller knows what went wrong */
1061 talloc_free(group_buffer);
1063 if (!grp) return false;
1068 #endif /* HAVE_GETGRNAM_R */
1069 #endif /* HAVE_GRP_H */