Zero log.indent in child requests
[freeradius.git] / src / main / util.c
1 /*
2  * util.c       Various utility functions.
3  *
4  * Version:     $Id$
5  *
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.
10  *
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.
15  *
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
19  *
20  * Copyright 2000,2006  The FreeRADIUS server project
21  */
22
23 RCSID("$Id$")
24
25 #include <freeradius-devel/radiusd.h>
26 #include <freeradius-devel/rad_assert.h>
27
28 #include <ctype.h>
29
30 #include <sys/stat.h>
31 #include <fcntl.h>
32
33 /*
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).
38  *
39  *      The same problem appears on HPUX, so we avoid it, if we can.
40  *
41  *      Using sigaction() to reset the signal handler fixes the problem,
42  *      so where available, we prefer that solution.
43  */
44
45 void (*reset_signal(int signo, void (*func)(int)))(int)
46 {
47 #ifdef HAVE_SIGACTION
48         struct sigaction act, oact;
49
50         memset(&act, 0, sizeof(act));
51         act.sa_handler = func;
52         sigemptyset(&act.sa_mask);
53         act.sa_flags = 0;
54 #ifdef  SA_INTERRUPT            /* SunOS */
55         act.sa_flags |= SA_INTERRUPT;
56 #endif
57         if (sigaction(signo, &act, &oact) < 0)
58                 return SIG_ERR;
59         return oact.sa_handler;
60 #else
61
62         /*
63          *      re-set by calling the 'signal' function, which
64          *      may cause infinite recursion and core dumps due to
65          *      stack growth.
66          *
67          *      However, the system is too dumb to implement sigaction(),
68          *      so we don't have a choice.
69          */
70         signal(signo, func);
71
72         return NULL;
73 #endif
74 }
75
76 /*
77  *      Per-request data, added by modules...
78  */
79 struct request_data_t {
80         request_data_t  *next;
81
82         void            *unique_ptr;
83         int             unique_int;
84         void            *opaque;
85         bool            free_opaque;
86 };
87
88 /*
89  *      Add opaque data (with a "free" function) to a REQUEST.
90  *
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.
94  */
95 int request_data_add(REQUEST *request, void *unique_ptr, int unique_int, void *opaque, bool free_opaque)
96 {
97         request_data_t *this, **last, *next;
98
99         /*
100          *      Some simple sanity checks.
101          */
102         if (!request || !opaque) return -1;
103
104         this = next = NULL;
105         for (last = &(request->data);
106              *last != NULL;
107              last = &((*last)->next)) {
108                 if (((*last)->unique_ptr == unique_ptr) &&
109                     ((*last)->unique_int == unique_int)) {
110                         this = *last;
111                         next = this->next;
112
113                         /*
114                          *      If caller requires custom behaviour on free
115                          *      they must set a destructor.
116                          */
117                         if (this->opaque && this->free_opaque) talloc_free(this->opaque);
118
119                         break;  /* replace the existing entry */
120                 }
121         }
122
123         /*
124          *      Only alloc new memory if we're not replacing
125          *      an existing entry.
126          */
127         if (!this) this = talloc_zero(request, request_data_t);
128         if (!this) return -1;
129
130         this->next = next;
131         this->unique_ptr = unique_ptr;
132         this->unique_int = unique_int;
133         this->opaque = opaque;
134         this->free_opaque = free_opaque;
135
136         *last = this;
137
138         return 0;
139 }
140
141 /*
142  *      Get opaque data from a request.
143  */
144 void *request_data_get(REQUEST *request, void *unique_ptr, int unique_int)
145 {
146         request_data_t **last;
147
148         if (!request) return NULL;
149
150         for (last = &(request->data);
151              *last != NULL;
152              last = &((*last)->next)) {
153                 if (((*last)->unique_ptr == unique_ptr) &&
154                     ((*last)->unique_int == unique_int)) {
155                         request_data_t *this;
156                         void *ptr;
157
158                         this = *last;
159                         ptr = this->opaque;
160
161                         /*
162                          *      Remove the entry from the list, and free it.
163                          */
164                         *last = this->next;
165                         talloc_free(this);
166
167                         return ptr;             /* don't free it, the caller does that */
168                 }
169         }
170
171         return NULL;            /* wasn't found, too bad... */
172 }
173
174 /*
175  *      Get opaque data from a request without removing it.
176  */
177 void *request_data_reference(REQUEST *request, void *unique_ptr, int unique_int)
178 {
179         request_data_t **last;
180
181         for (last = &(request->data);
182              *last != NULL;
183              last = &((*last)->next)) {
184                 if (((*last)->unique_ptr == unique_ptr) &&
185                     ((*last)->unique_int == unique_int)) {
186                         return (*last)->opaque;
187                 }
188         }
189
190         return NULL;            /* wasn't found, too bad... */
191 }
192
193 /*
194  *      Create possibly many directories.
195  *
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.
199  */
200 int rad_mkdir(char *directory, mode_t mode)
201 {
202         int rcode;
203         char *p;
204
205         /*
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.
209          */
210         rcode = mkdir(directory, mode & 0777);
211         if (rcode < 0) {
212                 if (errno == EEXIST) {
213                         return 0; /* don't change permissions */
214                 }
215
216                 if (errno != ENOENT) {
217                         return rcode;
218                 }
219
220                 /*
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
225                  *      error occured.
226                  */
227                 p = strrchr(directory, FR_DIR_SEP);
228                 if (!p || (p == directory)) return -1;
229
230                 *p = '\0';
231                 rcode = rad_mkdir(directory, mode);
232                 if (rcode < 0) return rcode;
233
234                 /*
235                  *      Reset the directory path, and try again to
236                  *      make the directory.
237                  */
238                 *p = FR_DIR_SEP;
239                 rcode = mkdir(directory, mode & 0777);
240                 if (rcode < 0) return rcode;
241         } /* else we successfully created the directory */
242
243         /*
244          *      Set the permissions on the created directory.
245          */
246         return chmod(directory, mode);
247 }
248
249 /** Escapes the raw string such that it should be safe to use as part of a file path
250  *
251  * This function is designed to produce a string that's still readable but portable
252  * across the majority of file systems.
253  *
254  * For security reasons it cannot remove characters from the name, and must not allow
255  * collisions to occur between different strings.
256  *
257  * With that in mind '-' has been chosen as the escape character, and will be double
258  * escaped '-' -> '--' to avoid collisions.
259  *
260  * Escaping should be reversible if the original string needs to be extracted.
261  *
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.
264  *
265  * @note OSX/Unix/NTFS/VFAT have a max filename size of 255 bytes.
266  *
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).
272  */
273 size_t rad_filename_escape(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
274 {
275         size_t freespace = outlen;
276
277         while (*in != '\0') {
278                 size_t utf8_len;
279
280                 /*
281                  *      Encode multibyte UTF8 chars
282                  */
283                 utf8_len = fr_utf8_char((uint8_t const *) in);
284                 if (utf8_len > 1) {
285                         if (freespace <= (utf8_len * 3)) break;
286
287                         switch (utf8_len) {
288                         case 2:
289                                 snprintf(out, freespace, "-%x-%x", in[0], in[1]);
290                                 break;
291
292                         case 3:
293                                 snprintf(out, freespace, "-%x-%x-%x", in[0], in[1], in[2]);
294                                 break;
295
296                         case 4:
297                                 snprintf(out, freespace, "-%x-%x-%x-%x", in[0], in[1], in[2], in[3]);
298                                 break;
299                         }
300
301                         freespace -= (utf8_len * 3);
302                         out += (utf8_len * 3);
303                         in += utf8_len;
304
305                         continue;
306                 }
307
308                 /*
309                  *      Safe chars
310                  */
311                 if (((*in >= 'A') && (*in <= 'Z')) ||
312                     ((*in >= 'a') && (*in <= 'z')) ||
313                     ((*in >= '0') && (*in <= '9')) ||
314                     (*in == '_') || (*in == '.')) {
315                         if (freespace <= 1) break;
316
317                         *out++ = *in++;
318                         freespace--;
319                         continue;
320                 }
321
322                 if (freespace <= 2) break;
323
324                 /*
325                  *      Double escape '-' (like \\)
326                  */
327                 if (*in == '-') {
328                         *out++ = '-';
329                         *out++ = '-';
330
331                         freespace -= 2;
332                         in++;
333                         continue;
334                 }
335
336                 /*
337                  *      Unsafe chars
338                  */
339                 *out++ = '-';
340                 fr_bin2hex(out, (uint8_t const *)in++, 1);
341                 out += 2;
342                 freespace -= 3;
343         }
344         *out = '\0';
345
346         return outlen - freespace;
347 }
348
349 /** Converts data stored in a file name back to its original form
350  *
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.
357  */
358 ssize_t rad_filename_unescape(char *out, size_t outlen, char const *in, size_t inlen)
359 {
360         char const *p, *end = in + inlen;
361         size_t freespace = outlen;
362
363         for (p = in; p < end; p++) {
364                 if (freespace <= 1) break;
365
366                 if (((*p >= 'A') && (*p <= 'Z')) ||
367                     ((*p >= 'a') && (*p <= 'z')) ||
368                     ((*p >= '0') && (*p <= '9')) ||
369                     (*p == '_') || (*p == '.')) {
370                         *out++ = *p;
371                         freespace--;
372                         continue;
373                 }
374
375                 if (p[0] == '-') {
376                         /*
377                          *      End of input, '-' needs at least one extra char after
378                          *      it to be valid.
379                          */
380                         if ((end - p) < 2) return in - p;
381                         if (p[1] == '-') {
382                                 p++;
383                                 *out++ = '-';
384                                 freespace--;
385                                 continue;
386                         }
387
388                         /*
389                          *      End of input, '-' must be followed by <hex><hex>
390                          *      but there aren't enough chars left
391                          */
392                         if ((end - p) < 3) return in - p;
393
394                         /*
395                          *      If hex2bin returns 0 the next two chars weren't hexits.
396                          */
397                         if (fr_hex2bin((uint8_t *) out, 1, in, 1) == 0) return in - (p + 1);
398                         in += 2;
399                         out++;
400                         freespace--;
401                 }
402
403                 return in - p; /* offset we found the bad char at */
404         }
405         *out = '\0';
406
407         return outlen - freespace;      /* how many bytes were written */
408 }
409
410 /*
411  *      Allocate memory, or exit.
412  *
413  *      This call ALWAYS succeeds!
414  */
415 void *rad_malloc(size_t size)
416 {
417         void *ptr = malloc(size);
418
419         if (ptr == NULL) {
420                 ERROR("no memory");
421                 fr_exit(1);
422         }
423
424         return ptr;
425 }
426
427
428 void rad_const_free(void const *ptr)
429 {
430         void *tmp;
431         if (!ptr) return;
432
433         memcpy(&tmp, &ptr, sizeof(tmp));
434         talloc_free(tmp);
435 }
436
437
438 /*
439  *      Logs an error message and aborts the program
440  *
441  */
442
443 void NEVER_RETURNS rad_assert_fail(char const *file, unsigned int line, char const *expr)
444 {
445         ERROR("ASSERT FAILED %s[%u]: %s", file, line, expr);
446         fr_fault(SIGABRT);
447         fr_exit_now(1);
448 }
449
450 /*
451  *      Free a REQUEST struct.
452  */
453 static int _request_free(REQUEST *request)
454 {
455         rad_assert(!request->in_request_hash);
456 #ifdef WITH_PROXY
457         rad_assert(!request->in_proxy_hash);
458 #endif
459         rad_assert(!request->ev);
460
461 #ifdef WITH_COA
462         if (request->coa) {
463                 request->coa->parent = NULL;
464         }
465
466         if (request->parent && (request->parent->coa == request)) {
467                 request->parent->coa = NULL;
468         }
469 #endif
470
471 #ifndef NDEBUG
472         request->magic = 0x01020304;    /* set the request to be nonsense */
473 #endif
474         request->client = NULL;
475 #ifdef WITH_PROXY
476         request->home_server = NULL;
477 #endif
478
479         return 0;
480 }
481
482 /*
483  *      Create a new REQUEST data structure.
484  */
485 REQUEST *request_alloc(TALLOC_CTX *ctx)
486 {
487         REQUEST *request;
488
489         request = talloc_zero(ctx, REQUEST);
490         talloc_set_destructor(request, _request_free);
491 #ifndef NDEBUG
492         request->magic = REQUEST_MAGIC;
493 #endif
494 #ifdef WITH_PROXY
495         request->proxy = NULL;
496 #endif
497         request->reply = NULL;
498 #ifdef WITH_PROXY
499         request->proxy_reply = NULL;
500 #endif
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 */
506
507         request->module = "";
508         request->component = "<core>";
509         request->log.func = vradlog_request;
510
511         return request;
512 }
513
514
515 /*
516  *      Create a new REQUEST, based on an old one.
517  *
518  *      This function allows modules to inject fake requests
519  *      into the server, for tunneled protocols like TTLS & PEAP.
520  */
521 REQUEST *request_alloc_fake(REQUEST *request)
522 {
523         REQUEST *fake;
524
525         fake = request_alloc(request);
526
527         fake->number = request->number;
528 #ifdef HAVE_PTHREAD_H
529         fake->child_pid = request->child_pid;
530 #endif
531         fake->parent = request;
532         fake->root = request->root;
533         fake->client = request->client;
534
535         /*
536          *      For new server support.
537          *
538          *      FIXME: Key instead off of a "virtual server" data structure.
539          *
540          *      FIXME: Permit different servers for inner && outer sessions?
541          */
542         fake->server = request->server;
543
544         fake->packet = rad_alloc(fake, true);
545         if (!fake->packet) {
546                 talloc_free(fake);
547                 return NULL;
548         }
549
550         fake->reply = rad_alloc(fake, false);
551         if (!fake->reply) {
552                 talloc_free(fake);
553                 return NULL;
554         }
555
556         fake->master_state = REQUEST_ACTIVE;
557         fake->child_state = REQUEST_RUNNING;
558
559         /*
560          *      Fill in the fake request.
561          */
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;
567
568         /*
569          *      This isn't STRICTLY required, as the fake request MUST NEVER
570          *      be put into the request list.  However, it's still reasonable
571          *      practice.
572          */
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;
577
578         /*
579          *      Required for new identity support
580          */
581         fake->listener = request->listener;
582
583         /*
584          *      Fill in the fake reply, based on the fake request.
585          */
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 */
593
594         /*
595          *      Copy debug information.
596          */
597         memcpy(&(fake->log), &(request->log), sizeof(fake->log));
598         fake->log.indent = 0;   /* Apart from the indent which we reset */
599
600         return fake;
601 }
602
603 #ifdef WITH_COA
604 REQUEST *request_alloc_coa(REQUEST *request)
605 {
606         if (!request || request->coa) return NULL;
607
608         /*
609          *      Originate CoA requests only when necessary.
610          */
611         if ((request->packet->code != PW_CODE_ACCESS_REQUEST) &&
612             (request->packet->code != PW_CODE_ACCOUNTING_REQUEST)) return NULL;
613
614         request->coa = request_alloc_fake(request);
615         if (!request->coa) return NULL;
616
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);
623                 return NULL;
624         }
625
626         return request->coa;
627 }
628 #endif
629
630 /*
631  *      Copy a quoted string.
632  */
633 int rad_copy_string(char *to, char const *from)
634 {
635         int length = 0;
636         char quote = *from;
637
638         do {
639                 if (*from == '\\') {
640                         *(to++) = *(from++);
641                         length++;
642                 }
643                 *(to++) = *(from++);
644                 length++;
645         } while (*from && (*from != quote));
646
647         if (*from != quote) return -1; /* not properly quoted */
648
649         *(to++) = quote;
650         length++;
651         *to = '\0';
652
653         return length;
654 }
655
656 /*
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.
660  */
661 int rad_copy_string_bare(char *to, char const *from)
662 {
663         int length = 0;
664         char quote = *from;
665
666         from++;
667         while (*from && (*from != quote)) {
668                 if (*from == '\\') {
669                         *(to++) = *(from++);
670                         length++;
671                 }
672                 *(to++) = *(from++);
673                 length++;
674         }
675
676         if (*from != quote) return -1; /* not properly quoted */
677
678         *to = '\0';
679
680         return length;
681 }
682
683
684 /*
685  *      Copy a %{} string.
686  */
687 int rad_copy_variable(char *to, char const *from)
688 {
689         int length = 0;
690         int sublen;
691
692         *(to++) = *(from++);
693         length++;
694
695         while (*from) {
696                 switch (*from) {
697                 case '"':
698                 case '\'':
699                         sublen = rad_copy_string(to, from);
700                         if (sublen < 0) return sublen;
701                         from += sublen;
702                         to += sublen;
703                         length += sublen;
704                         break;
705
706                 case '}':       /* end of variable expansion */
707                         *(to++) = *(from++);
708                         *to = '\0';
709                         length++;
710                         return length; /* proper end of variable */
711
712                 case '\\':
713                         *(to++) = *(from++);
714                         *(to++) = *(from++);
715                         length += 2;
716                         break;
717
718                 case '%':       /* start of variable expansion */
719                         if (from[1] == '{') {
720                                 *(to++) = *(from++);
721                                 length++;
722
723                                 sublen = rad_copy_variable(to, from);
724                                 if (sublen < 0) return sublen;
725                                 from += sublen;
726                                 to += sublen;
727                                 length += sublen;
728                                 break;
729                         } /* else FIXME: catch %%{ ?*/
730
731                         /* FALL-THROUGH */
732                 default:
733                         *(to++) = *(from++);
734                         length++;
735                         break;
736                 }
737         } /* loop over the input string */
738
739         /*
740          *      We ended the string before a trailing '}'
741          */
742
743         return -1;
744 }
745
746 #ifndef USEC
747 #define USEC 1000000
748 #endif
749
750 uint32_t rad_pps(uint32_t *past, uint32_t *present, time_t *then, struct timeval *now)
751 {
752         uint32_t pps;
753
754         if (*then != now->tv_sec) {
755                 *then = now->tv_sec;
756                 *past = *present;
757                 *present = 0;
758         }
759
760         /*
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
766          *      doing:
767          *
768          *      PPS = pps_now + pps_old * (1 - f)
769          *
770          *      It's an instantaneous measurement, rather than
771          *      a moving average.  This will hopefully let it
772          *      respond better to sudden spikes.
773          *
774          *      Doing the calculations by thousands allows us
775          *      to not overflow 2^32, AND to not underflow
776          *      when we divide by USEC.
777          */
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 */
783
784         return pps;
785 }
786
787 /** Split string into words and expand each one
788  *
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.
798  */
799
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)
803 {
804         char const *from;
805         char *to;
806         int argc = -1;
807         int i;
808         int left;
809
810         if (strlen(cmd) > (argv_buflen - 1)) {
811                 ERROR("rad_expand_xlat: Command line is too long");
812                 return -1;
813         }
814
815         /*
816          *      Check for bad escapes.
817          */
818         if (cmd[strlen(cmd) - 1] == '\\') {
819                 ERROR("rad_expand_xlat: Command line has final backslash, without a following character");
820                 return -1;
821         }
822
823         strlcpy(argv_buf, cmd, argv_buflen);
824
825         /*
826          *      Split the string into argv's BEFORE doing radius_xlat...
827          */
828         from = cmd;
829         to = argv_buf;
830         argc = 0;
831         while (*from) {
832                 int length;
833
834                 /*
835                  *      Skip spaces.
836                  */
837                 if ((*from == ' ') || (*from == '\t')) {
838                         from++;
839                         continue;
840                 }
841
842                 argv[argc] = to;
843                 argc++;
844
845                 if (argc >= (max_argc - 1)) break;
846
847                 /*
848                  *      Copy the argv over to our buffer.
849                  */
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");
853                                 return -1;
854                         }
855
856                         switch (*from) {
857                         case '"':
858                         case '\'':
859                                 length = rad_copy_string_bare(to, from);
860                                 if (length < 0) {
861                                         ERROR("rad_expand_xlat: Invalid string passed as argument");
862                                         return -1;
863                                 }
864                                 from += length+2;
865                                 to += length;
866                                 break;
867
868                         case '%':
869                                 if (from[1] == '{') {
870                                         *(to++) = *(from++);
871
872                                         length = rad_copy_variable(to, from);
873                                         if (length < 0) {
874                                                 ERROR("rad_expand_xlat: Invalid variable expansion passed as argument");
875                                                 return -1;
876                                         }
877                                         from += length;
878                                         to += length;
879                                 } else { /* FIXME: catch %%{ ? */
880                                         *(to++) = *(from++);
881                                 }
882                                 break;
883
884                         case '\\':
885                                 if (from[1] == ' ') from++;
886                                 /* FALL-THROUGH */
887
888                         default:
889                                 *(to++) = *(from++);
890                         }
891                 } /* end of string, or found a space */
892
893                 *(to++) = '\0'; /* terminate the string */
894         }
895
896         /*
897          *      We have to have SOMETHING, at least.
898          */
899         if (argc <= 0) {
900                 ERROR("rad_expand_xlat: Empty command line");
901                 return -1;
902         }
903
904         /*
905          *      Expand each string, as appropriate.
906          */
907         left = argv_buf + argv_buflen - to;
908         for (i = 0; i < argc; i++) {
909                 int sublen;
910
911                 /*
912                  *      Don't touch argv's which won't be translated.
913                  */
914                 if (strchr(argv[i], '%') == NULL) continue;
915
916                 if (!request) continue;
917
918                 sublen = radius_xlat(to, left - 1, request, argv[i], NULL, NULL);
919                 if (sublen <= 0) {
920                         if (can_fail) {
921                                 /*
922                                  *      Fail to be backwards compatible.
923                                  *
924                                  *      It's yucky, but it won't break anything,
925                                  *      and it won't cause security problems.
926                                  */
927                                 sublen = 0;
928                         } else {
929                                 ERROR("rad_expand_xlat: xlat failed");
930                                 return -1;
931                         }
932                 }
933
934                 argv[i] = to;
935                 to += sublen;
936                 *(to++) = '\0';
937                 left -= sublen;
938                 left--;
939
940                 if (left <= 0) {
941                         ERROR("rad_expand_xlat: Ran out of space while expanding arguments");
942                         return -1;
943                 }
944         }
945         argv[argc] = NULL;
946
947         return argc;
948 }
949
950 #ifndef NDEBUG
951 /*
952  *      Verify a packet.
953  */
954 static void verify_packet(char const *file, int line, REQUEST *request, RADIUS_PACKET *packet, char const *type)
955 {
956         TALLOC_CTX *parent;
957
958         if (!packet) {
959                 fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%i]: RADIUS_PACKET %s pointer was NULL", file, line, type);
960                 fr_assert(0);
961                 fr_exit_now(0);
962         }
963
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");
969
970                 fr_log_talloc_report(packet);
971                 if (parent) fr_log_talloc_report(parent);
972
973                 rad_assert(0);
974         }
975
976         VERIFY_PACKET(packet);
977
978         if (!packet->vps) return;
979
980 #ifdef WITH_VERIFY_PTR
981         fr_verify_list(file, line, packet, packet->vps);
982 #endif
983 }
984 /*
985  *      Catch horrible talloc errors.
986  */
987 void verify_request(char const *file, int line, REQUEST *request)
988 {
989         if (!request) {
990                 fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%i]: REQUEST pointer was NULL", file, line);
991                 fr_assert(0);
992                 fr_exit_now(0);
993         }
994
995         (void) talloc_get_type_abort(request, REQUEST);
996
997 #ifdef WITH_VERIFY_PTR
998         fr_verify_list(file, line, request, request->config_items);
999         fr_verify_list(file, line, request, request->state);
1000 #endif
1001
1002         if (request->packet) verify_packet(file, line, request, request->packet, "request");
1003         if (request->reply) verify_packet(file, line, request, request->reply, "reply");
1004 #ifdef WITH_PROXY
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");
1007 #endif
1008
1009 #ifdef WITH_COA
1010         if (request->coa) {
1011                 void *parent;
1012
1013                 (void) talloc_get_type_abort(request->coa, REQUEST);
1014                 parent = talloc_parent(request->coa);
1015
1016                 rad_assert(parent == request);
1017
1018                 verify_request(file, line, request->coa);
1019         }
1020 #endif
1021 }
1022 #endif
1023
1024 #ifdef HAVE_GRP_H
1025 #ifndef HAVE_GETGRNAM_R
1026 bool fr_getgid(char const *name, gid_t *gid)
1027 {
1028         struct group *grp;
1029
1030         grp =  getgrnam(name);
1031         if (!grp) return false;
1032
1033         *gid = grp->gr_gid;
1034
1035         return true;
1036 }
1037 #else  /* getgrnam_r() exists */
1038
1039 bool fr_getgid(char const *name, gid_t *gid)
1040 {
1041         struct group    *grp, my_group;
1042         char            *group_buffer;
1043         size_t          group_size = 1024;
1044
1045         grp = NULL;
1046         group_buffer = talloc_array(NULL, char, group_size);
1047         while (group_buffer) {
1048                 int err;
1049
1050                 err = getgrnam_r(name, &my_group, group_buffer, group_size, &grp);
1051                 if (err == ERANGE) {
1052                         group_size *= 2;
1053                         group_buffer = talloc_realloc(NULL, group_buffer, char, group_size);
1054                         continue;
1055                 }
1056
1057                 if (err) errno = err; /* so the caller knows what went wrong */
1058
1059                 break;
1060         }
1061
1062         talloc_free(group_buffer);
1063
1064         if (!grp) return false;
1065
1066         *gid = grp->gr_gid;
1067         return true;
1068 }
1069 #endif  /* HAVE_GETGRNAM_R */
1070 #endif  /* HAVE_GRP_H */