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));
598 fake->log.indent = 0; /* Apart from the indent which we reset */
604 REQUEST *request_alloc_coa(REQUEST *request)
606 if (!request || request->coa) return NULL;
609 * Originate CoA requests only when necessary.
611 if ((request->packet->code != PW_CODE_ACCESS_REQUEST) &&
612 (request->packet->code != PW_CODE_ACCOUNTING_REQUEST)) return NULL;
614 request->coa = request_alloc_fake(request);
615 if (!request->coa) return NULL;
617 request->coa->options = 1; /* is a CoA packet */
618 request->coa->packet->code = 0; /* unknown, as of yet */
619 request->coa->child_state = REQUEST_RUNNING;
620 request->coa->proxy = rad_alloc(request->coa, false);
621 if (!request->coa->proxy) {
622 TALLOC_FREE(request->coa);
631 * Copy a quoted string.
633 int rad_copy_string(char *to, char const *from)
645 } while (*from && (*from != quote));
647 if (*from != quote) return -1; /* not properly quoted */
657 * Copy a quoted string but without the quotes. The length
658 * returned is the number of chars written; the number of
659 * characters consumed is 2 more than this.
661 int rad_copy_string_bare(char *to, char const *from)
667 while (*from && (*from != quote)) {
676 if (*from != quote) return -1; /* not properly quoted */
687 int rad_copy_variable(char *to, char const *from)
699 sublen = rad_copy_string(to, from);
700 if (sublen < 0) return sublen;
706 case '}': /* end of variable expansion */
710 return length; /* proper end of variable */
718 case '%': /* start of variable expansion */
719 if (from[1] == '{') {
723 sublen = rad_copy_variable(to, from);
724 if (sublen < 0) return sublen;
729 } /* else FIXME: catch %%{ ?*/
737 } /* loop over the input string */
740 * We ended the string before a trailing '}'
750 uint32_t rad_pps(uint32_t *past, uint32_t *present, time_t *then, struct timeval *now)
754 if (*then != now->tv_sec) {
761 * Bootstrap PPS by looking at a percentage of
762 * the previous PPS. This lets us take a moving
763 * count, without doing a moving average. If
764 * we're a fraction "f" (0..1) into the current
765 * second, we can get a good guess for PPS by
768 * PPS = pps_now + pps_old * (1 - f)
770 * It's an instantaneous measurement, rather than
771 * a moving average. This will hopefully let it
772 * respond better to sudden spikes.
774 * Doing the calculations by thousands allows us
775 * to not overflow 2^32, AND to not underflow
776 * when we divide by USEC.
778 pps = USEC - now->tv_usec; /* useconds left in previous second */
779 pps /= 1000; /* scale to milliseconds */
780 pps *= *past; /* multiply by past count to get fraction */
781 pps /= 1000; /* scale to usec again */
782 pps += *present; /* add in current count */
787 /** Split string into words and expand each one
789 * @param request Current request.
790 * @param cmd string to split.
791 * @param max_argc the maximum number of arguments to split into.
792 * @param argv Where to write the pointers into argv_buf.
793 * @param can_fail If false, stop processing if any of the xlat expansions fail.
794 * @param argv_buflen size of argv_buf.
795 * @param argv_buf temporary buffer we used to mangle/expand cmd.
796 * Pointers to offsets of this buffer will be written to argv.
797 * @return argc or -1 on failure.
800 int rad_expand_xlat(REQUEST *request, char const *cmd,
801 int max_argc, char *argv[], bool can_fail,
802 size_t argv_buflen, char *argv_buf)
810 if (strlen(cmd) > (argv_buflen - 1)) {
811 ERROR("rad_expand_xlat: Command line is too long");
816 * Check for bad escapes.
818 if (cmd[strlen(cmd) - 1] == '\\') {
819 ERROR("rad_expand_xlat: Command line has final backslash, without a following character");
823 strlcpy(argv_buf, cmd, argv_buflen);
826 * Split the string into argv's BEFORE doing radius_xlat...
837 if ((*from == ' ') || (*from == '\t')) {
845 if (argc >= (max_argc - 1)) break;
848 * Copy the argv over to our buffer.
850 while (*from && (*from != ' ') && (*from != '\t')) {
851 if (to >= argv_buf + argv_buflen - 1) {
852 ERROR("rad_expand_xlat: Ran out of space in command line");
859 length = rad_copy_string_bare(to, from);
861 ERROR("rad_expand_xlat: Invalid string passed as argument");
869 if (from[1] == '{') {
872 length = rad_copy_variable(to, from);
874 ERROR("rad_expand_xlat: Invalid variable expansion passed as argument");
879 } else { /* FIXME: catch %%{ ? */
885 if (from[1] == ' ') from++;
891 } /* end of string, or found a space */
893 *(to++) = '\0'; /* terminate the string */
897 * We have to have SOMETHING, at least.
900 ERROR("rad_expand_xlat: Empty command line");
905 * Expand each string, as appropriate.
907 left = argv_buf + argv_buflen - to;
908 for (i = 0; i < argc; i++) {
912 * Don't touch argv's which won't be translated.
914 if (strchr(argv[i], '%') == NULL) continue;
916 if (!request) continue;
918 sublen = radius_xlat(to, left - 1, request, argv[i], NULL, NULL);
922 * Fail to be backwards compatible.
924 * It's yucky, but it won't break anything,
925 * and it won't cause security problems.
929 ERROR("rad_expand_xlat: xlat failed");
941 ERROR("rad_expand_xlat: Ran out of space while expanding arguments");
954 static void verify_packet(char const *file, int line, REQUEST *request, RADIUS_PACKET *packet, char const *type)
959 fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%i]: RADIUS_PACKET %s pointer was NULL", file, line, type);
964 parent = talloc_parent(packet);
965 if (parent != request) {
966 ERROR("CONSISTENCY CHECK FAILED %s[%i]: Expected RADIUS_PACKET %s to be parented by %p (%s), "
967 "but parented by %p (%s)", file, line, type, request, talloc_get_name(request),
968 parent, parent ? talloc_get_name(parent) : "NULL");
970 fr_log_talloc_report(packet);
971 if (parent) fr_log_talloc_report(parent);
976 VERIFY_PACKET(packet);
978 if (!packet->vps) return;
980 #ifdef WITH_VERIFY_PTR
981 fr_verify_list(file, line, packet, packet->vps);
985 * Catch horrible talloc errors.
987 void verify_request(char const *file, int line, REQUEST *request)
990 fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%i]: REQUEST pointer was NULL", file, line);
995 (void) talloc_get_type_abort(request, REQUEST);
997 #ifdef WITH_VERIFY_PTR
998 fr_verify_list(file, line, request, request->config_items);
999 fr_verify_list(file, line, request, request->state);
1002 if (request->packet) verify_packet(file, line, request, request->packet, "request");
1003 if (request->reply) verify_packet(file, line, request, request->reply, "reply");
1005 if (request->proxy) verify_packet(file, line, request, request->proxy, "proxy-request");
1006 if (request->proxy_reply) verify_packet(file, line, request, request->proxy_reply, "proxy-reply");
1013 (void) talloc_get_type_abort(request->coa, REQUEST);
1014 parent = talloc_parent(request->coa);
1016 rad_assert(parent == request);
1018 verify_request(file, line, request->coa);
1025 #ifndef HAVE_GETGRNAM_R
1026 bool fr_getgid(char const *name, gid_t *gid)
1030 grp = getgrnam(name);
1031 if (!grp) return false;
1037 #else /* getgrnam_r() exists */
1039 bool fr_getgid(char const *name, gid_t *gid)
1041 struct group *grp, my_group;
1043 size_t group_size = 1024;
1046 group_buffer = talloc_array(NULL, char, group_size);
1047 while (group_buffer) {
1050 err = getgrnam_r(name, &my_group, group_buffer, group_size, &grp);
1051 if (err == ERANGE) {
1053 group_buffer = talloc_realloc(NULL, group_buffer, char, group_size);
1057 if (err) errno = err; /* so the caller knows what went wrong */
1062 talloc_free(group_buffer);
1064 if (!grp) return false;
1069 #endif /* HAVE_GETGRNAM_R */
1070 #endif /* HAVE_GRP_H */