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... */
193 /** Create possibly many directories.
195 * @note that the input directory name is NOT treated as a constant. This is so that
196 * if an error is returned, the 'directory' ptr points to the name of the file
197 * which caused the error.
199 * @param dir path to directory to create.
200 * @param mode for new directories.
201 * @param uid to set on new directories, may be -1 to use effective uid.
202 * @param gid to set on new directories, may be -1 to use effective gid.
203 * @return 0 on success, -1 on error. Error available as errno.
205 int rad_mkdir(char *dir, mode_t mode, uid_t uid, gid_t gid)
211 * Try to make the dir. If it exists, chmod it.
212 * If a path doesn't exist, that's OK. Otherwise
213 * return with an error.
215 * Directories permissions are initially set so
216 * that only we should have access. This prevents
217 * an attacker removing them and swapping them
218 * out for a link to somewhere else.
219 * We change them to the correct permissions later.
221 rcode = mkdir(dir, 0700);
225 return 0; /* don't change permissions */
235 * A component in the dir path doesn't
236 * exist. Look for the LAST dir name. Try
237 * to create that. If there's an error, we leave
238 * the dir path as the one at which the
241 p = strrchr(dir, FR_DIR_SEP);
242 if (!p || (p == dir)) return -1;
245 rcode = rad_mkdir(dir, mode, uid, gid);
246 if (rcode < 0) return rcode;
249 * Reset the dir path, and try again to
253 rcode = mkdir(dir, 0700);
254 if (rcode < 0) return rcode;
255 } /* else we successfully created the dir */
258 * Set the permissions on the directory we created
259 * this should never fail unless there's a race.
261 fd = open(dir, O_DIRECTORY);
262 if (fd < 0) return -1;
264 rcode = fchmod(fd, mode);
270 if ((uid != (uid_t)-1) || (gid != (gid_t)-1)) {
272 rcode = fchown(fd, uid, gid);
280 /** Ensures that a filename cannot walk up the directory structure
282 * Also sanitizes control chars.
284 * @param request Current request (may be NULL).
285 * @param out Output buffer.
286 * @param outlen Size of the output buffer.
287 * @param in string to escape.
288 * @param arg Context arguments (unused, should be NULL).
290 size_t rad_filename_make_safe(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
294 size_t left = outlen;
301 * Smash control characters and spaces to
315 * For now, allow slashes in the expanded
316 * filename. This allows the admin to set
317 * attributes which create sub-directories.
318 * Unfortunately, it also allows users to send
319 * attributes which *may* end up creating
326 * Get rid of ////../.././///.///..//
347 * Get rid of /../../../
349 if ((q[0] == '.') && (q[1] == '.') &&
360 /** Escapes the raw string such that it should be safe to use as part of a file path
362 * This function is designed to produce a string that's still readable but portable
363 * across the majority of file systems.
365 * For security reasons it cannot remove characters from the name, and must not allow
366 * collisions to occur between different strings.
368 * With that in mind '-' has been chosen as the escape character, and will be double
369 * escaped '-' -> '--' to avoid collisions.
371 * Escaping should be reversible if the original string needs to be extracted.
373 * @note function takes additional arguments so that it may be used as an xlat escape
374 * function but it's fine to call it directly.
376 * @note OSX/Unix/NTFS/VFAT have a max filename size of 255 bytes.
378 * @param request Current request (may be NULL).
379 * @param out Output buffer.
380 * @param outlen Size of the output buffer.
381 * @param in string to escape.
382 * @param arg Context arguments (unused, should be NULL).
384 size_t rad_filename_escape(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
386 size_t freespace = outlen;
388 while (*in != '\0') {
392 * Encode multibyte UTF8 chars
394 utf8_len = fr_utf8_char((uint8_t const *) in, -1);
396 if (freespace <= (utf8_len * 3)) break;
400 snprintf(out, freespace, "-%x-%x", in[0], in[1]);
404 snprintf(out, freespace, "-%x-%x-%x", in[0], in[1], in[2]);
408 snprintf(out, freespace, "-%x-%x-%x-%x", in[0], in[1], in[2], in[3]);
412 freespace -= (utf8_len * 3);
413 out += (utf8_len * 3);
422 if (((*in >= 'A') && (*in <= 'Z')) ||
423 ((*in >= 'a') && (*in <= 'z')) ||
424 ((*in >= '0') && (*in <= '9')) ||
426 if (freespace <= 1) break;
432 if (freespace <= 2) break;
435 * Double escape '-' (like \\)
450 fr_bin2hex(out, (uint8_t const *)in++, 1);
456 return outlen - freespace;
459 /** Converts data stored in a file name back to its original form
461 * @param out Where to write the unescaped string (may be the same as in).
462 * @param outlen Length of the output buffer.
463 * @param in Input filename.
464 * @param inlen Length of input.
465 * @return number of bytes written to output buffer, or offset where parse error
466 * occurred on failure.
468 ssize_t rad_filename_unescape(char *out, size_t outlen, char const *in, size_t inlen)
470 char const *p, *end = in + inlen;
471 size_t freespace = outlen;
473 for (p = in; p < end; p++) {
474 if (freespace <= 1) break;
476 if (((*p >= 'A') && (*p <= 'Z')) ||
477 ((*p >= 'a') && (*p <= 'z')) ||
478 ((*p >= '0') && (*p <= '9')) ||
487 * End of input, '-' needs at least one extra char after
490 if ((end - p) < 2) return in - p;
499 * End of input, '-' must be followed by <hex><hex>
500 * but there aren't enough chars left
502 if ((end - p) < 3) return in - p;
505 * If hex2bin returns 0 the next two chars weren't hexits.
507 if (fr_hex2bin((uint8_t *) out, 1, in, 1) == 0) return in - (p + 1);
513 return in - p; /* offset we found the bad char at */
517 return outlen - freespace; /* how many bytes were written */
521 * Allocate memory, or exit.
523 * This call ALWAYS succeeds!
525 void *rad_malloc(size_t size)
527 void *ptr = malloc(size);
538 void rad_const_free(void const *ptr)
543 memcpy(&tmp, &ptr, sizeof(tmp));
549 * Logs an error message and aborts the program
553 void NEVER_RETURNS rad_assert_fail(char const *file, unsigned int line, char const *expr)
555 ERROR("ASSERT FAILED %s[%u]: %s", file, line, expr);
561 * Free a REQUEST struct.
563 static int _request_free(REQUEST *request)
565 rad_assert(!request->in_request_hash);
567 rad_assert(!request->in_proxy_hash);
569 rad_assert(!request->ev);
572 rad_assert(request->coa == NULL);
576 request->magic = 0x01020304; /* set the request to be nonsense */
578 request->client = NULL;
580 request->home_server = NULL;
587 * Create a new REQUEST data structure.
589 REQUEST *request_alloc(TALLOC_CTX *ctx)
593 request = talloc_zero(ctx, REQUEST);
594 if (!request) return NULL;
595 talloc_set_destructor(request, _request_free);
597 request->magic = REQUEST_MAGIC;
600 request->proxy = NULL;
602 request->reply = NULL;
604 request->proxy_reply = NULL;
606 request->config = NULL;
607 request->username = NULL;
608 request->password = NULL;
609 request->timestamp = time(NULL);
610 request->log.lvl = rad_debug_lvl; /* Default to global debug level */
612 request->module = "";
613 request->component = "<core>";
614 request->log.func = vradlog_request;
621 * Create a new REQUEST, based on an old one.
623 * This function allows modules to inject fake requests
624 * into the server, for tunneled protocols like TTLS & PEAP.
626 REQUEST *request_alloc_fake(REQUEST *request)
630 fake = request_alloc(request);
631 if (!fake) return NULL;
633 fake->number = request->number;
634 #ifdef HAVE_PTHREAD_H
635 fake->child_pid = request->child_pid;
637 fake->parent = request;
638 fake->root = request->root;
639 fake->client = request->client;
642 * For new server support.
644 * FIXME: Key instead off of a "virtual server" data structure.
646 * FIXME: Permit different servers for inner && outer sessions?
648 fake->server = request->server;
650 fake->packet = rad_alloc(fake, true);
656 fake->reply = rad_alloc(fake, false);
662 fake->master_state = REQUEST_ACTIVE;
663 fake->child_state = REQUEST_RUNNING;
666 * Fill in the fake request.
668 fake->packet->sockfd = -1;
669 fake->packet->src_ipaddr = request->packet->src_ipaddr;
670 fake->packet->src_port = request->packet->src_port;
671 fake->packet->dst_ipaddr = request->packet->dst_ipaddr;
672 fake->packet->dst_port = 0;
675 * This isn't STRICTLY required, as the fake request MUST NEVER
676 * be put into the request list. However, it's still reasonable
679 fake->packet->id = fake->number & 0xff;
680 fake->packet->code = request->packet->code;
681 fake->timestamp = request->timestamp;
682 fake->packet->timestamp = request->packet->timestamp;
685 * Required for new identity support
687 fake->listener = request->listener;
690 * Fill in the fake reply, based on the fake request.
692 fake->reply->sockfd = fake->packet->sockfd;
693 fake->reply->src_ipaddr = fake->packet->dst_ipaddr;
694 fake->reply->src_port = fake->packet->dst_port;
695 fake->reply->dst_ipaddr = fake->packet->src_ipaddr;
696 fake->reply->dst_port = fake->packet->src_port;
697 fake->reply->id = fake->packet->id;
698 fake->reply->code = 0; /* UNKNOWN code */
701 * Copy debug information.
703 memcpy(&(fake->log), &(request->log), sizeof(fake->log));
704 fake->log.indent = 0; /* Apart from the indent which we reset */
710 REQUEST *request_alloc_coa(REQUEST *request)
712 if (!request || request->coa) return NULL;
715 * Originate CoA requests only when necessary.
717 if ((request->packet->code != PW_CODE_ACCESS_REQUEST) &&
718 (request->packet->code != PW_CODE_ACCOUNTING_REQUEST)) return NULL;
720 request->coa = request_alloc_fake(request);
721 if (!request->coa) return NULL;
723 request->coa->options = RAD_REQUEST_OPTION_COA; /* is a CoA packet */
724 request->coa->packet->code = 0; /* unknown, as of yet */
725 request->coa->child_state = REQUEST_RUNNING;
726 request->coa->proxy = rad_alloc(request->coa, false);
727 if (!request->coa->proxy) {
728 TALLOC_FREE(request->coa);
737 * Copy a quoted string.
739 int rad_copy_string(char *to, char const *from)
751 } while (*from && (*from != quote));
753 if (*from != quote) return -1; /* not properly quoted */
763 * Copy a quoted string but without the quotes. The length
764 * returned is the number of chars written; the number of
765 * characters consumed is 2 more than this.
767 int rad_copy_string_bare(char *to, char const *from)
773 while (*from && (*from != quote)) {
782 if (*from != quote) return -1; /* not properly quoted */
793 int rad_copy_variable(char *to, char const *from)
805 sublen = rad_copy_string(to, from);
806 if (sublen < 0) return sublen;
812 case '}': /* end of variable expansion */
816 return length; /* proper end of variable */
824 case '%': /* start of variable expansion */
825 if (from[1] == '{') {
829 sublen = rad_copy_variable(to, from);
830 if (sublen < 0) return sublen;
835 } /* else FIXME: catch %%{ ?*/
843 } /* loop over the input string */
846 * We ended the string before a trailing '}'
856 uint32_t rad_pps(uint32_t *past, uint32_t *present, time_t *then, struct timeval *now)
860 if (*then != now->tv_sec) {
867 * Bootstrap PPS by looking at a percentage of
868 * the previous PPS. This lets us take a moving
869 * count, without doing a moving average. If
870 * we're a fraction "f" (0..1) into the current
871 * second, we can get a good guess for PPS by
874 * PPS = pps_now + pps_old * (1 - f)
876 * It's an instantaneous measurement, rather than
877 * a moving average. This will hopefully let it
878 * respond better to sudden spikes.
880 * Doing the calculations by thousands allows us
881 * to not overflow 2^32, AND to not underflow
882 * when we divide by USEC.
884 pps = USEC - now->tv_usec; /* useconds left in previous second */
885 pps /= 1000; /* scale to milliseconds */
886 pps *= *past; /* multiply by past count to get fraction */
887 pps /= 1000; /* scale to usec again */
888 pps += *present; /* add in current count */
893 /** Split string into words and expand each one
895 * @param request Current request.
896 * @param cmd string to split.
897 * @param max_argc the maximum number of arguments to split into.
898 * @param argv Where to write the pointers into argv_buf.
899 * @param can_fail If false, stop processing if any of the xlat expansions fail.
900 * @param argv_buflen size of argv_buf.
901 * @param argv_buf temporary buffer we used to mangle/expand cmd.
902 * Pointers to offsets of this buffer will be written to argv.
903 * @return argc or -1 on failure.
906 int rad_expand_xlat(REQUEST *request, char const *cmd,
907 int max_argc, char const *argv[], bool can_fail,
908 size_t argv_buflen, char *argv_buf)
916 if (strlen(cmd) > (argv_buflen - 1)) {
917 ERROR("rad_expand_xlat: Command line is too long");
922 * Check for bad escapes.
924 if (cmd[strlen(cmd) - 1] == '\\') {
925 ERROR("rad_expand_xlat: Command line has final backslash, without a following character");
929 strlcpy(argv_buf, cmd, argv_buflen);
932 * Split the string into argv's BEFORE doing radius_xlat...
943 if ((*from == ' ') || (*from == '\t')) {
951 if (argc >= (max_argc - 1)) break;
954 * Copy the argv over to our buffer.
956 while (*from && (*from != ' ') && (*from != '\t')) {
957 if (to >= argv_buf + argv_buflen - 1) {
958 ERROR("rad_expand_xlat: Ran out of space in command line");
965 length = rad_copy_string_bare(to, from);
967 ERROR("rad_expand_xlat: Invalid string passed as argument");
975 if (from[1] == '{') {
978 length = rad_copy_variable(to, from);
980 ERROR("rad_expand_xlat: Invalid variable expansion passed as argument");
985 } else { /* FIXME: catch %%{ ? */
991 if (from[1] == ' ') from++;
997 } /* end of string, or found a space */
999 *(to++) = '\0'; /* terminate the string */
1003 * We have to have SOMETHING, at least.
1006 ERROR("rad_expand_xlat: Empty command line");
1011 * Expand each string, as appropriate.
1013 left = argv_buf + argv_buflen - to;
1014 for (i = 0; i < argc; i++) {
1018 * Don't touch argv's which won't be translated.
1020 if (strchr(argv[i], '%') == NULL) continue;
1022 if (!request) continue;
1024 sublen = radius_xlat(to, left - 1, request, argv[i], NULL, NULL);
1028 * Fail to be backwards compatible.
1030 * It's yucky, but it won't break anything,
1031 * and it won't cause security problems.
1035 ERROR("rad_expand_xlat: xlat failed");
1047 ERROR("rad_expand_xlat: Ran out of space while expanding arguments");
1060 static void verify_packet(char const *file, int line, REQUEST *request, RADIUS_PACKET *packet, char const *type)
1065 fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%i]: RADIUS_PACKET %s pointer was NULL", file, line, type);
1070 parent = talloc_parent(packet);
1071 if (parent != request) {
1072 ERROR("CONSISTENCY CHECK FAILED %s[%i]: Expected RADIUS_PACKET %s to be parented by %p (%s), "
1073 "but parented by %p (%s)", file, line, type, request, talloc_get_name(request),
1074 parent, parent ? talloc_get_name(parent) : "NULL");
1076 fr_log_talloc_report(packet);
1077 if (parent) fr_log_talloc_report(parent);
1082 VERIFY_PACKET(packet);
1084 if (!packet->vps) return;
1086 #ifdef WITH_VERIFY_PTR
1087 fr_pair_list_verify(file, line, packet, packet->vps);
1091 * Catch horrible talloc errors.
1093 void verify_request(char const *file, int line, REQUEST *request)
1096 fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%i]: REQUEST pointer was NULL", file, line);
1101 (void) talloc_get_type_abort(request, REQUEST);
1103 #ifdef WITH_VERIFY_PTR
1104 fr_pair_list_verify(file, line, request, request->config);
1105 fr_pair_list_verify(file, line, request, request->state);
1108 if (request->packet) verify_packet(file, line, request, request->packet, "request");
1109 if (request->reply) verify_packet(file, line, request, request->reply, "reply");
1111 if (request->proxy) verify_packet(file, line, request, request->proxy, "proxy-request");
1112 if (request->proxy_reply) verify_packet(file, line, request, request->proxy_reply, "proxy-reply");
1119 (void) talloc_get_type_abort(request->coa, REQUEST);
1120 parent = talloc_parent(request->coa);
1122 rad_assert(parent == request);
1124 verify_request(file, line, request->coa);
1130 /** Convert mode_t into humanly readable permissions flags
1132 * @author Jonathan Leffler.
1134 * @param mode to convert.
1135 * @param out Where to write the string to, must be exactly 10 bytes long.
1137 void rad_mode_to_str(char out[10], mode_t mode)
1139 static char const *rwx[] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"};
1141 strcpy(&out[0], rwx[(mode >> 6) & 0x07]);
1142 strcpy(&out[3], rwx[(mode >> 3) & 0x07]);
1143 strcpy(&out[6], rwx[(mode & 7)]);
1144 if (mode & S_ISUID) out[2] = (mode & 0100) ? 's' : 'S';
1145 if (mode & S_ISGID) out[5] = (mode & 0010) ? 's' : 'l';
1146 if (mode & S_ISVTX) out[8] = (mode & 0100) ? 't' : 'T';
1150 void rad_mode_to_oct(char out[5], mode_t mode)
1152 out[0] = '0' + ((mode >> 9) & 0x07);
1153 out[1] = '0' + ((mode >> 6) & 0x07);
1154 out[2] = '0' + ((mode >> 3) & 0x07);
1155 out[3] = '0' + (mode & 0x07);
1159 /** Resolve a uid to a passwd entry
1161 * Resolves a uid to a passwd entry. The memory to hold the
1162 * passwd entry is talloced under ctx, and must be freed when no
1165 * @param ctx to allocate passwd entry in.
1166 * @param out Where to write pointer to entry.
1167 * @param uid to resolve.
1168 * @return 0 on success, -1 on error.
1170 int rad_getpwuid(TALLOC_CTX *ctx, struct passwd **out, uid_t uid)
1179 * We assume this won't change between calls,
1180 * and that the value is the same, so races don't
1184 #ifdef _SC_GETPW_R_SIZE_MAX
1187 sc_len = sysconf(_SC_GETPW_R_SIZE_MAX);
1188 if (sc_len <= 0) sc_len = 1024;
1189 len = (size_t)sc_len;
1195 buff = talloc_array(ctx, uint8_t, sizeof(struct passwd) + len);
1196 if (!buff) return -1;
1199 * In some cases we may need to dynamically
1200 * grow the string buffer.
1202 while ((ret = getpwuid_r(uid, (struct passwd *)buff, (char *)(buff + sizeof(struct passwd)),
1203 talloc_array_length(buff) - sizeof(struct passwd), out)) == ERANGE) {
1204 buff = talloc_realloc_size(ctx, buff, talloc_array_length(buff) * 2);
1211 if ((ret != 0) || !*out) {
1212 fr_strerror_printf("Failed resolving UID: %s", fr_syserror(ret));
1218 talloc_set_type(buff, struct passwd);
1219 *out = (struct passwd *)buff;
1224 /** Resolve a username to a passwd entry
1226 * Resolves a username to a passwd entry. The memory to hold the
1227 * passwd entry is talloced under ctx, and must be freed when no
1230 * @param ctx to allocate passwd entry in.
1231 * @param out Where to write pointer to entry.
1232 * @param name to resolve.
1233 * @return 0 on success, -1 on error.
1235 int rad_getpwnam(TALLOC_CTX *ctx, struct passwd **out, char const *name)
1244 * We assume this won't change between calls,
1245 * and that the value is the same, so races don't
1249 #ifdef _SC_GETPW_R_SIZE_MAX
1252 sc_len = sysconf(_SC_GETPW_R_SIZE_MAX);
1253 if (sc_len <= 0) sc_len = 1024;
1254 len = (size_t)sc_len;
1260 buff = talloc_array(ctx, uint8_t, sizeof(struct passwd) + len);
1261 if (!buff) return -1;
1264 * In some cases we may need to dynamically
1265 * grow the string buffer.
1267 while ((ret = getpwnam_r(name, (struct passwd *)buff, (char *)(buff + sizeof(struct passwd)),
1268 talloc_array_length(buff) - sizeof(struct passwd), out)) == ERANGE) {
1269 buff = talloc_realloc_size(ctx, buff, talloc_array_length(buff) * 2);
1276 if ((ret != 0) || !*out) {
1277 fr_strerror_printf("Failed resolving UID: %s", fr_syserror(ret));
1283 talloc_set_type(buff, struct passwd);
1284 *out = (struct passwd *)buff;
1289 /** Resolve a gid to a group database entry
1291 * Resolves a gid to a group database entry. The memory to hold the
1292 * group entry is talloced under ctx, and must be freed when no
1295 * @param ctx to allocate passwd entry in.
1296 * @param out Where to write pointer to entry.
1297 * @param gid to resolve.
1298 * @return 0 on success, -1 on error.
1300 int rad_getgrgid(TALLOC_CTX *ctx, struct group **out, gid_t gid)
1309 * We assume this won't change between calls,
1310 * and that the value is the same, so races don't
1314 #ifdef _SC_GETGR_R_SIZE_MAX
1317 sc_len = sysconf(_SC_GETGR_R_SIZE_MAX);
1318 if (sc_len <= 0) sc_len = 1024;
1319 len = (size_t)sc_len;
1325 buff = talloc_array(ctx, uint8_t, sizeof(struct group) + len);
1326 if (!buff) return -1;
1329 * In some cases we may need to dynamically
1330 * grow the string buffer.
1332 while ((ret = getgrgid_r(gid, (struct group *)buff, (char *)(buff + sizeof(struct group)),
1333 talloc_array_length(buff) - sizeof(struct group), out)) == ERANGE) {
1334 buff = talloc_realloc_size(ctx, buff, talloc_array_length(buff) * 2);
1341 if ((ret != 0) || !*out) {
1342 fr_strerror_printf("Failed resolving GID: %s", fr_syserror(ret));
1348 talloc_set_type(buff, struct group);
1349 *out = (struct group *)buff;
1354 /** Resolve a group name to a group database entry
1356 * Resolves a group name to a group database entry.
1357 * The memory to hold the group entry is talloced under ctx,
1358 * and must be freed when no longer required.
1360 * @param ctx to allocate passwd entry in.
1361 * @param out Where to write pointer to entry.
1362 * @param name to resolve.
1363 * @return 0 on success, -1 on error.
1365 int rad_getgrnam(TALLOC_CTX *ctx, struct group **out, char const *name)
1374 * We assume this won't change between calls,
1375 * and that the value is the same, so races don't
1379 #ifdef _SC_GETGR_R_SIZE_MAX
1382 sc_len = sysconf(_SC_GETGR_R_SIZE_MAX);
1383 if (sc_len <= 0) sc_len = 1024;
1384 len = (size_t)sc_len;
1390 buff = talloc_array(ctx, uint8_t, sizeof(struct group) + len);
1391 if (!buff) return -1;
1394 * In some cases we may need to dynamically
1395 * grow the string buffer.
1397 while ((ret = getgrnam_r(name, (struct group *)buff, (char *)(buff + sizeof(struct group)),
1398 talloc_array_length(buff) - sizeof(struct group), out)) == ERANGE) {
1399 buff = talloc_realloc_size(ctx, buff, talloc_array_length(buff) * 2);
1406 if ((ret != 0) || !*out) {
1407 fr_strerror_printf("Failed resolving GID: %s", fr_syserror(ret));
1413 talloc_set_type(buff, struct group);
1414 *out = (struct group *)buff;
1419 /** Resolve a group name to a GID
1421 * @param ctx TALLOC_CTX for temporary allocations.
1422 * @param name of group.
1423 * @param out where to write gid.
1424 * @return 0 on success, -1 on error;
1426 int rad_getgid(TALLOC_CTX *ctx, gid_t *out, char const *name)
1429 struct group *result;
1431 ret = rad_getgrnam(ctx, &result, name);
1432 if (ret < 0) return -1;
1434 *out = result->gr_gid;
1435 talloc_free(result);
1439 /** Print uid to a string
1441 * @note The reason for taking a fixed buffer is pure laziness.
1442 * It means the caller doesn't have to free the string.
1444 * @note Will always \0 terminate the buffer, even on error.
1446 * @param ctx TALLOC_CTX for temporary allocations.
1447 * @param out Where to write the uid string.
1448 * @param outlen length of output buffer.
1449 * @param uid to resolve.
1450 * @return 0 on success, -1 on failure.
1452 int rad_prints_uid(TALLOC_CTX *ctx, char *out, size_t outlen, uid_t uid)
1454 struct passwd *result;
1456 rad_assert(outlen > 0);
1460 if (rad_getpwuid(ctx, &result, uid) < 0) return -1;
1461 strlcpy(out, result->pw_name, outlen);
1462 talloc_free(result);
1467 /** Print gid to a string
1469 * @note The reason for taking a fixed buffer is pure laziness.
1470 * It means the caller doesn't have to free the string.
1472 * @note Will always \0 terminate the buffer, even on error.
1474 * @param ctx TALLOC_CTX for temporary allocations.
1475 * @param out Where to write the uid string.
1476 * @param outlen length of output buffer.
1477 * @param gid to resolve.
1478 * @return 0 on success, -1 on failure.
1480 int rad_prints_gid(TALLOC_CTX *ctx, char *out, size_t outlen, gid_t gid)
1482 struct group *result;
1484 rad_assert(outlen > 0);
1488 if (rad_getgrgid(ctx, &result, gid) < 0) return -1;
1489 strlcpy(out, result->gr_name, outlen);
1490 talloc_free(result);
1496 static bool doing_setuid = false;
1497 static uid_t suid_down_uid = (uid_t)-1;
1499 /** Set the uid and gid used when dropping privileges
1501 * @note if this function hasn't been called, rad_suid_down will have no effect.
1503 * @param uid to drop down to.
1505 void rad_suid_set_down_uid(uid_t uid)
1507 suid_down_uid = uid;
1508 doing_setuid = true;
1511 # if defined(HAVE_SETRESUID) && defined (HAVE_GETRESUID)
1512 void rad_suid_up(void)
1514 uid_t ruid, euid, suid;
1516 if (getresuid(&ruid, &euid, &suid) < 0) {
1517 ERROR("Failed getting saved UID's");
1521 if (setresuid(-1, suid, -1) < 0) {
1522 ERROR("Failed switching to privileged user");
1526 if (geteuid() != suid) {
1527 ERROR("Switched to unknown UID");
1532 void rad_suid_down(void)
1534 if (!doing_setuid) return;
1536 if (setresuid(-1, suid_down_uid, geteuid()) < 0) {
1537 struct passwd *passwd;
1540 name = (rad_getpwuid(NULL, &passwd, suid_down_uid) < 0) ? "unknown" : passwd->pw_name;
1541 ERROR("Failed switching to uid %s: %s", name, fr_syserror(errno));
1542 talloc_free(passwd);
1546 if (geteuid() != suid_down_uid) {
1547 ERROR("Failed switching uid: UID is incorrect");
1551 fr_reset_dumpable();
1554 void rad_suid_down_permanent(void)
1556 if (!doing_setuid) return;
1558 if (setresuid(suid_down_uid, suid_down_uid, suid_down_uid) < 0) {
1559 struct passwd *passwd;
1562 name = (rad_getpwuid(NULL, &passwd, suid_down_uid) < 0) ? "unknown" : passwd->pw_name;
1563 ERROR("Failed in permanent switch to uid %s: %s", name, fr_syserror(errno));
1564 talloc_free(passwd);
1568 if (geteuid() != suid_down_uid) {
1569 ERROR("Switched to unknown uid");
1573 fr_reset_dumpable();
1577 * Much less secure...
1579 void rad_suid_up(void)
1581 if (!doing_setuid) return;
1583 if (seteuid(0) < 0) {
1584 ERROR("Failed switching up to euid 0: %s", fr_syserror(errno));
1590 void rad_suid_down(void)
1592 if (!doing_setuid) return;
1594 if (geteuid() == suid_down_uid) return;
1596 if (seteuid(suid_down_uid) < 0) {
1597 struct passwd *passwd;
1600 name = (rad_getpwuid(NULL, &passwd, suid_down_uid) < 0) ? "unknown": passwd->pw_name;
1601 ERROR("Failed switching to euid %s: %s", name, fr_syserror(errno));
1602 talloc_free(passwd);
1606 fr_reset_dumpable();
1609 void rad_suid_down_permanent(void)
1611 if (!doing_setuid) return;
1614 * Already done. Don't do anything else.
1616 if (getuid() == suid_down_uid) return;
1619 * We're root, but running as a normal user. Fix that,
1620 * so we can call setuid().
1622 if (geteuid() == suid_down_uid) {
1626 if (setuid(suid_down_uid) < 0) {
1627 struct passwd *passwd;
1630 name = (rad_getpwuid(NULL, &passwd, suid_down_uid) < 0) ? "unknown": passwd->pw_name;
1631 ERROR("Failed switching permanently to uid %s: %s", name, fr_syserror(errno));
1632 talloc_free(passwd);
1636 fr_reset_dumpable();
1638 # endif /* HAVE_SETRESUID && HAVE_GETRESUID */
1639 #else /* HAVE_SETUID */
1640 void rad_suid_set_down_uid(uid_t uid)
1643 void rad_suid_up(void)
1646 void rad_suid_down(void)
1648 fr_reset_dumpable();
1650 void rad_suid_down_permanent(void)
1652 fr_reset_dumpable();
1654 #endif /* HAVE_SETUID */
1656 /** Alter the effective user id
1659 * @return 0 on success -1 on failure.
1661 int rad_seuid(uid_t uid)
1663 if (seteuid(uid) < 0) {
1664 struct passwd *passwd;
1666 if (rad_getpwuid(NULL, &passwd, uid) < 0) return -1;
1667 fr_strerror_printf("Failed setting euid to %s", passwd->pw_name);
1668 talloc_free(passwd);
1675 /** Alter the effective user id
1678 * @return 0 on success -1 on failure.
1680 int rad_segid(gid_t gid)
1682 if (setegid(gid) < 0) {
1683 struct group *group;
1685 if (rad_getgrgid(NULL, &group, gid) < 0) return -1;
1686 fr_strerror_printf("Failed setting egid to %s", group->gr_name);