704727ae1b91d5bbe2ec38794391c58d230fd173
[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
599         return fake;
600 }
601
602 #ifdef WITH_COA
603 REQUEST *request_alloc_coa(REQUEST *request)
604 {
605         if (!request || request->coa) return NULL;
606
607         /*
608          *      Originate CoA requests only when necessary.
609          */
610         if ((request->packet->code != PW_CODE_ACCESS_REQUEST) &&
611             (request->packet->code != PW_CODE_ACCOUNTING_REQUEST)) return NULL;
612
613         request->coa = request_alloc_fake(request);
614         if (!request->coa) return NULL;
615
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);
622                 return NULL;
623         }
624
625         return request->coa;
626 }
627 #endif
628
629 /*
630  *      Copy a quoted string.
631  */
632 int rad_copy_string(char *to, char const *from)
633 {
634         int length = 0;
635         char quote = *from;
636
637         do {
638                 if (*from == '\\') {
639                         *(to++) = *(from++);
640                         length++;
641                 }
642                 *(to++) = *(from++);
643                 length++;
644         } while (*from && (*from != quote));
645
646         if (*from != quote) return -1; /* not properly quoted */
647
648         *(to++) = quote;
649         length++;
650         *to = '\0';
651
652         return length;
653 }
654
655 /*
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.
659  */
660 int rad_copy_string_bare(char *to, char const *from)
661 {
662         int length = 0;
663         char quote = *from;
664
665         from++;
666         while (*from && (*from != quote)) {
667                 if (*from == '\\') {
668                         *(to++) = *(from++);
669                         length++;
670                 }
671                 *(to++) = *(from++);
672                 length++;
673         }
674
675         if (*from != quote) return -1; /* not properly quoted */
676
677         *to = '\0';
678
679         return length;
680 }
681
682
683 /*
684  *      Copy a %{} string.
685  */
686 int rad_copy_variable(char *to, char const *from)
687 {
688         int length = 0;
689         int sublen;
690
691         *(to++) = *(from++);
692         length++;
693
694         while (*from) {
695                 switch (*from) {
696                 case '"':
697                 case '\'':
698                         sublen = rad_copy_string(to, from);
699                         if (sublen < 0) return sublen;
700                         from += sublen;
701                         to += sublen;
702                         length += sublen;
703                         break;
704
705                 case '}':       /* end of variable expansion */
706                         *(to++) = *(from++);
707                         *to = '\0';
708                         length++;
709                         return length; /* proper end of variable */
710
711                 case '\\':
712                         *(to++) = *(from++);
713                         *(to++) = *(from++);
714                         length += 2;
715                         break;
716
717                 case '%':       /* start of variable expansion */
718                         if (from[1] == '{') {
719                                 *(to++) = *(from++);
720                                 length++;
721
722                                 sublen = rad_copy_variable(to, from);
723                                 if (sublen < 0) return sublen;
724                                 from += sublen;
725                                 to += sublen;
726                                 length += sublen;
727                                 break;
728                         } /* else FIXME: catch %%{ ?*/
729
730                         /* FALL-THROUGH */
731                 default:
732                         *(to++) = *(from++);
733                         length++;
734                         break;
735                 }
736         } /* loop over the input string */
737
738         /*
739          *      We ended the string before a trailing '}'
740          */
741
742         return -1;
743 }
744
745 #ifndef USEC
746 #define USEC 1000000
747 #endif
748
749 uint32_t rad_pps(uint32_t *past, uint32_t *present, time_t *then, struct timeval *now)
750 {
751         uint32_t pps;
752
753         if (*then != now->tv_sec) {
754                 *then = now->tv_sec;
755                 *past = *present;
756                 *present = 0;
757         }
758
759         /*
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
765          *      doing:
766          *
767          *      PPS = pps_now + pps_old * (1 - f)
768          *
769          *      It's an instantaneous measurement, rather than
770          *      a moving average.  This will hopefully let it
771          *      respond better to sudden spikes.
772          *
773          *      Doing the calculations by thousands allows us
774          *      to not overflow 2^32, AND to not underflow
775          *      when we divide by USEC.
776          */
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 */
782
783         return pps;
784 }
785
786 /** Split string into words and expand each one
787  *
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.
797  */
798
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)
802 {
803         char const *from;
804         char *to;
805         int argc = -1;
806         int i;
807         int left;
808
809         if (strlen(cmd) > (argv_buflen - 1)) {
810                 ERROR("rad_expand_xlat: Command line is too long");
811                 return -1;
812         }
813
814         /*
815          *      Check for bad escapes.
816          */
817         if (cmd[strlen(cmd) - 1] == '\\') {
818                 ERROR("rad_expand_xlat: Command line has final backslash, without a following character");
819                 return -1;
820         }
821
822         strlcpy(argv_buf, cmd, argv_buflen);
823
824         /*
825          *      Split the string into argv's BEFORE doing radius_xlat...
826          */
827         from = cmd;
828         to = argv_buf;
829         argc = 0;
830         while (*from) {
831                 int length;
832
833                 /*
834                  *      Skip spaces.
835                  */
836                 if ((*from == ' ') || (*from == '\t')) {
837                         from++;
838                         continue;
839                 }
840
841                 argv[argc] = to;
842                 argc++;
843
844                 if (argc >= (max_argc - 1)) break;
845
846                 /*
847                  *      Copy the argv over to our buffer.
848                  */
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");
852                                 return -1;
853                         }
854
855                         switch (*from) {
856                         case '"':
857                         case '\'':
858                                 length = rad_copy_string_bare(to, from);
859                                 if (length < 0) {
860                                         ERROR("rad_expand_xlat: Invalid string passed as argument");
861                                         return -1;
862                                 }
863                                 from += length+2;
864                                 to += length;
865                                 break;
866
867                         case '%':
868                                 if (from[1] == '{') {
869                                         *(to++) = *(from++);
870
871                                         length = rad_copy_variable(to, from);
872                                         if (length < 0) {
873                                                 ERROR("rad_expand_xlat: Invalid variable expansion passed as argument");
874                                                 return -1;
875                                         }
876                                         from += length;
877                                         to += length;
878                                 } else { /* FIXME: catch %%{ ? */
879                                         *(to++) = *(from++);
880                                 }
881                                 break;
882
883                         case '\\':
884                                 if (from[1] == ' ') from++;
885                                 /* FALL-THROUGH */
886
887                         default:
888                                 *(to++) = *(from++);
889                         }
890                 } /* end of string, or found a space */
891
892                 *(to++) = '\0'; /* terminate the string */
893         }
894
895         /*
896          *      We have to have SOMETHING, at least.
897          */
898         if (argc <= 0) {
899                 ERROR("rad_expand_xlat: Empty command line");
900                 return -1;
901         }
902
903         /*
904          *      Expand each string, as appropriate.
905          */
906         left = argv_buf + argv_buflen - to;
907         for (i = 0; i < argc; i++) {
908                 int sublen;
909
910                 /*
911                  *      Don't touch argv's which won't be translated.
912                  */
913                 if (strchr(argv[i], '%') == NULL) continue;
914
915                 if (!request) continue;
916
917                 sublen = radius_xlat(to, left - 1, request, argv[i], NULL, NULL);
918                 if (sublen <= 0) {
919                         if (can_fail) {
920                                 /*
921                                  *      Fail to be backwards compatible.
922                                  *
923                                  *      It's yucky, but it won't break anything,
924                                  *      and it won't cause security problems.
925                                  */
926                                 sublen = 0;
927                         } else {
928                                 ERROR("rad_expand_xlat: xlat failed");
929                                 return -1;
930                         }
931                 }
932
933                 argv[i] = to;
934                 to += sublen;
935                 *(to++) = '\0';
936                 left -= sublen;
937                 left--;
938
939                 if (left <= 0) {
940                         ERROR("rad_expand_xlat: Ran out of space while expanding arguments");
941                         return -1;
942                 }
943         }
944         argv[argc] = NULL;
945
946         return argc;
947 }
948
949 #ifndef NDEBUG
950 /*
951  *      Verify a packet.
952  */
953 static void verify_packet(char const *file, int line, REQUEST *request, RADIUS_PACKET *packet, char const *type)
954 {
955         TALLOC_CTX *parent;
956
957         if (!packet) {
958                 fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%i]: RADIUS_PACKET %s pointer was NULL", file, line, type);
959                 fr_assert(0);
960                 fr_exit_now(0);
961         }
962
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");
968
969                 fr_log_talloc_report(packet);
970                 if (parent) fr_log_talloc_report(parent);
971
972                 rad_assert(0);
973         }
974
975         VERIFY_PACKET(packet);
976
977         if (!packet->vps) return;
978
979 #ifdef WITH_VERIFY_PTR
980         fr_verify_list(file, line, packet, packet->vps);
981 #endif
982 }
983 /*
984  *      Catch horrible talloc errors.
985  */
986 void verify_request(char const *file, int line, REQUEST *request)
987 {
988         if (!request) {
989                 fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%i]: REQUEST pointer was NULL", file, line);
990                 fr_assert(0);
991                 fr_exit_now(0);
992         }
993
994         (void) talloc_get_type_abort(request, REQUEST);
995
996 #ifdef WITH_VERIFY_PTR
997         fr_verify_list(file, line, request, request->config_items);
998         fr_verify_list(file, line, request, request->state);
999 #endif
1000
1001         if (request->packet) verify_packet(file, line, request, request->packet, "request");
1002         if (request->reply) verify_packet(file, line, request, request->reply, "reply");
1003 #ifdef WITH_PROXY
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");
1006 #endif
1007
1008 #ifdef WITH_COA
1009         if (request->coa) {
1010                 void *parent;
1011
1012                 (void) talloc_get_type_abort(request->coa, REQUEST);
1013                 parent = talloc_parent(request->coa);
1014
1015                 rad_assert(parent == request);
1016
1017                 verify_request(file, line, request->coa);
1018         }
1019 #endif
1020 }
1021 #endif
1022
1023 #ifdef HAVE_GRP_H
1024 #ifndef HAVE_GETGRNAM_R
1025 bool fr_getgid(char const *name, gid_t *gid)
1026 {
1027         struct group *grp;
1028
1029         grp =  getgrnam(name);
1030         if (!grp) return false;
1031
1032         *gid = grp->gr_gid;
1033
1034         return true;
1035 }
1036 #else  /* getgrnam_r() exists */
1037
1038 bool fr_getgid(char const *name, gid_t *gid)
1039 {
1040         struct group    *grp, my_group;
1041         char            *group_buffer;
1042         size_t          group_size = 1024;
1043
1044         grp = NULL;
1045         group_buffer = talloc_array(NULL, char, group_size);
1046         while (group_buffer) {
1047                 int err;
1048
1049                 err = getgrnam_r(name, &my_group, group_buffer, group_size, &grp);
1050                 if (err == ERANGE) {
1051                         group_size *= 2;
1052                         group_buffer = talloc_realloc(NULL, group_buffer, char, group_size);
1053                         continue;
1054                 }
1055
1056                 if (err) errno = err; /* so the caller knows what went wrong */
1057
1058                 break;
1059         }
1060
1061         talloc_free(group_buffer);
1062
1063         if (!grp) return false;
1064
1065         *gid = grp->gr_gid;
1066         return true;
1067 }
1068 #endif  /* HAVE_GETGRNAM_R */
1069 #endif  /* HAVE_GRP_H */