All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
files.c
Go to the documentation of this file.
1 /*
2  * files.c Read config files into memory.
3  *
4  * Version: $Id: 14d77ebacbb50e0bd9f1deab397e21dc0822779d $
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  * Copyright 2000 Miquel van Smoorenburg <miquels@cistron.nl>
22  * Copyright 2000 Alan DeKok <aland@ox.org>
23  */
24 
25 RCSID("$Id: 14d77ebacbb50e0bd9f1deab397e21dc0822779d $")
26 
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/rad_assert.h>
29 
30 #include <sys/stat.h>
31 
32 #include <ctype.h>
33 #include <fcntl.h>
34 
35 /*
36  * Debug code.
37  */
38 #if 0
39 static void debug_pair_list(PAIR_LIST *pl)
40 {
41  VALUE_PAIR *vp;
42 
43  while(pl) {
44  printf("Pair list: %s\n", pl->name);
45  printf("** Check:\n");
46  for(vp = pl->check; vp; vp = vp->next) {
47  printf(" ");
48  fr_pair_fprint(stdout, vp);
49  printf("\n");
50  }
51  printf("** Reply:\n");
52  for(vp = pl->reply; vp; vp = vp->next) {
53  printf(" ");
54  fr_pair_fprint(stdout, vp);
55  printf("\n");
56  }
57  pl = pl->next;
58  }
59 }
60 #endif
61 
62 /*
63  * Free a PAIR_LIST
64  */
66 {
67  talloc_free(*pl);
68  *pl = NULL;
69 }
70 
71 
72 #define FIND_MODE_NAME 0
73 #define FIND_MODE_WANT_REPLY 1
74 #define FIND_MODE_HAVE_REPLY 2
75 
76 /*
77  * Read the users, huntgroups or hints file.
78  * Return a PAIR_LIST.
79  */
80 int pairlist_read(TALLOC_CTX *ctx, char const *file, PAIR_LIST **list, int complain)
81 {
82  FILE *fp;
83  int mode = FIND_MODE_NAME;
84  char entry[256];
85  char buffer[8192];
86  char const *ptr;
87  VALUE_PAIR *check_tmp = NULL;
88  VALUE_PAIR *reply_tmp = NULL;
89  PAIR_LIST *pl = NULL, *t;
90  PAIR_LIST **last = &pl;
91  int lineno = 0;
92  int entry_lineno = 0;
93  FR_TOKEN parsecode;
94 #ifdef HAVE_REGEX_H
95  VALUE_PAIR *vp;
96  vp_cursor_t cursor;
97 #endif
98  char newfile[8192];
99 
100  DEBUG2("reading pairlist file %s", file);
101 
102  /*
103  * Open the file. The error message should be a little
104  * more useful...
105  */
106  if ((fp = fopen(file, "r")) == NULL) {
107  if (!complain)
108  return -1;
109  ERROR("Couldn't open %s for reading: %s", file, fr_syserror(errno));
110  return -1;
111  }
112 
113  /*
114  * Read the entire file into memory for speed.
115  */
116  while (fgets(buffer, sizeof(buffer), fp) != NULL) {
117  lineno++;
118 
119  if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
120  fclose(fp);
121  ERROR("%s[%d]: line too long", file, lineno);
122  pairlist_free(&pl);
123  return -1;
124  }
125 
126  /*
127  * If the line contains nothing but whitespace,
128  * ignore it.
129  */
130  ptr = buffer;
131  while (isspace((int) *ptr)) ptr++;
132 
133  if (*ptr == '#' || *ptr == '\n' || !*ptr) continue;
134 
135 parse_again:
136  if (mode == FIND_MODE_NAME) {
137  /*
138  * The user's name MUST be the first text on the line.
139  */
140  if (isspace((int) buffer[0])) {
141  ERROR("%s[%d]: Entry does not begin with a user name",
142  file, lineno);
143  fclose(fp);
144  return -1;
145  }
146 
147  /*
148  * Get the name.
149  */
150  ptr = buffer;
151  getword(&ptr, entry, sizeof(entry), false);
152  entry_lineno = lineno;
153 
154  /*
155  * Include another file if we see
156  * $INCLUDE filename
157  */
158  if (strcasecmp(entry, "$INCLUDE") == 0) {
159  while (isspace((int) *ptr)) ptr++;
160 
161  /*
162  * If it's an absolute pathname,
163  * then use it verbatim.
164  *
165  * If not, then make the $include
166  * files *relative* to the current
167  * file.
168  */
169  if (FR_DIR_IS_RELATIVE(ptr)) {
170  char *p;
171 
172  strlcpy(newfile, file,
173  sizeof(newfile));
174  p = strrchr(newfile, FR_DIR_SEP);
175  if (!p) {
176  p = newfile + strlen(newfile);
177  *p = FR_DIR_SEP;
178  }
179  getword(&ptr, p + 1, sizeof(newfile) - 1 - (p - newfile), false);
180  } else {
181  getword(&ptr, newfile, sizeof(newfile), false);
182  }
183 
184  t = NULL;
185 
186  if (pairlist_read(ctx, newfile, &t, 0) != 0) {
187  pairlist_free(&pl);
188  ERROR("%s[%d]: Could not open included file %s: %s",
189  file, lineno, newfile, fr_syserror(errno));
190  fclose(fp);
191  return -1;
192  }
193  *last = t;
194 
195  /*
196  * t may be NULL, it may have one
197  * entry, or it may be a linked list
198  * of entries. Go to the end of the
199  * list.
200  */
201  while (*last)
202  last = &((*last)->next);
203  continue;
204  } /* $INCLUDE ... */
205 
206  /*
207  * Parse the check values
208  */
209  rad_assert(check_tmp == NULL);
210  rad_assert(reply_tmp == NULL);
211  parsecode = fr_pair_list_afrom_str(ctx, ptr, &check_tmp);
212  if (parsecode == T_INVALID) {
213  pairlist_free(&pl);
214  ERROR("%s[%d]: Parse error (check) for entry %s: %s",
215  file, lineno, entry, fr_strerror());
216  fclose(fp);
217  return -1;
218  }
219 
220  if (parsecode != T_EOL) {
221  pairlist_free(&pl);
222  talloc_free(check_tmp);
223  ERROR("%s[%d]: Invalid text after check attributes for entry %s",
224  file, lineno, entry);
225  fclose(fp);
226  return -1;
227  }
228 
229 #ifdef HAVE_REGEX_H
230  /*
231  * Do some more sanity checks.
232  */
233  for (vp = fr_cursor_init(&cursor, &check_tmp);
234  vp;
235  vp = fr_cursor_next(&cursor)) {
236  if (((vp->op == T_OP_REG_EQ) ||
237  (vp->op == T_OP_REG_NE)) &&
238  (vp->da->type != PW_TYPE_STRING)) {
239  pairlist_free(&pl);
240  talloc_free(check_tmp);
241  ERROR("%s[%d]: Cannot use regular expressions for non-string attributes in entry %s",
242  file, lineno, entry);
243  fclose(fp);
244  return -1;
245  }
246  }
247 #endif
248 
249  /*
250  * The reply MUST be on a new line.
251  */
252  mode = FIND_MODE_WANT_REPLY;
253  continue;
254  }
255 
256  /*
257  * We COULD have a reply, OR we could have a new entry.
258  */
259  if (mode == FIND_MODE_WANT_REPLY) {
260  if (!isspace((int) buffer[0])) goto create_entry;
261 
262  mode = FIND_MODE_HAVE_REPLY;
263  }
264 
265  /*
266  * mode == FIND_MODE_HAVE_REPLY
267  */
268 
269  /*
270  * The previous line ended with a comma, and then
271  * we have the start of a new entry!
272  */
273  if (!isspace((int) buffer[0])) {
274  trailing_comma:
275  pairlist_free(&pl);
276  talloc_free(check_tmp);
277  talloc_free(reply_tmp);
278  ERROR("%s[%d]: Invalid comma after the reply attributes. Please delete it.",
279  file, lineno);
280  fclose(fp);
281  return -1;
282  }
283 
284  /*
285  * Parse the reply values. If there's a trailing
286  * comma, keep parsing the reply values.
287  */
288  parsecode = fr_pair_list_afrom_str(ctx, buffer, &reply_tmp);
289  if (parsecode == T_COMMA) {
290  continue;
291  }
292 
293  /*
294  * We expect an EOL. Anything else is an error.
295  */
296  if (parsecode != T_EOL) {
297  pairlist_free(&pl);
298  talloc_free(check_tmp);
299  talloc_free(reply_tmp);
300  ERROR("%s[%d]: Parse error (reply) for entry %s: %s",
301  file, lineno, entry, fr_strerror());
302  fclose(fp);
303  return -1;
304  }
305 
306  create_entry:
307  /*
308  * Done with this entry...
309  */
310  MEM(t = talloc_zero(ctx, PAIR_LIST));
311 
312  if (check_tmp) fr_pair_steal(t, check_tmp);
313  if (reply_tmp) fr_pair_steal(t, reply_tmp);
314 
315  t->check = check_tmp;
316  t->reply = reply_tmp;
317  t->lineno = entry_lineno;
318  check_tmp = NULL;
319  reply_tmp = NULL;
320 
321  t->name = talloc_typed_strdup(t, entry);
322 
323  *last = t;
324  last = &(t->next);
325 
326  /*
327  * Look for a name. If we came here because
328  * there were no reply attributes, then re-parse
329  * the current line, instead of reading another one.
330  */
331  mode = FIND_MODE_NAME;
332  if (feof(fp)) break;
333  if (!isspace((int) buffer[0])) goto parse_again;
334  }
335 
336  /*
337  * We're at EOF. If we're supposed to read more, that's
338  * an error.
339  */
340  if (mode == FIND_MODE_HAVE_REPLY) goto trailing_comma;
341 
342  /*
343  * We had an entry, but no reply attributes. That's OK.
344  */
345  if (mode == FIND_MODE_WANT_REPLY) goto create_entry;
346 
347  /*
348  * Else we were looking for an entry. We didn't get one
349  * because we were at EOF, so that's OK.
350  */
351 
352  fclose(fp);
353 
354  *list = pl;
355  return 0;
356 }
char const * name
Definition: tmpl.h:120
Definition: token.h:34
VALUE_PAIR * check
Definition: tmpl.h:121
void pairlist_free(PAIR_LIST **pl)
Definition: files.c:65
#define MEM(x)
Definition: radiusd.h:396
void fr_pair_steal(TALLOC_CTX *ctx, VALUE_PAIR *vp)
Steal one VP.
Definition: pair.c:184
Definition: token.h:39
VALUE_PAIR * fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR *const *node)
Setup a cursor to iterate over attribute pairs.
Definition: cursor.c:60
void fr_pair_fprint(FILE *, VALUE_PAIR const *vp)
Print one attribute and value to FP.
Definition: pair.c:2232
int pairlist_read(TALLOC_CTX *ctx, char const *file, PAIR_LIST **list, int complain)
Definition: files.c:80
Abstraction to allow iterating over different configurations of VALUE_PAIRs.
Definition: pair.h:144
struct value_pair * next
Definition: pair.h:116
#define rad_assert(expr)
Definition: rad_assert.h:38
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: log.c:238
struct pair_list * next
Definition: tmpl.h:124
#define DEBUG2(fmt,...)
Definition: log.h:176
#define FIND_MODE_WANT_REPLY
Definition: files.c:73
int getword(char const **ptr, char *buf, int buflen, bool unescape)
Definition: token.c:396
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
#define FIND_MODE_HAVE_REPLY
Definition: files.c:74
FR_TOKEN op
Operator to use when moving or inserting valuepair into a list.
Definition: pair.h:118
char const * fr_strerror(void)
Get the last library error.
Definition: log.c:212
int strcasecmp(char *s1, char *s2)
Definition: missing.c:73
VALUE_PAIR * fr_cursor_next(vp_cursor_t *cursor)
Advanced the cursor to the next VALUE_PAIR.
Definition: cursor.c:263
static rbtree_t * pl
Definition: process.c:53
enum fr_token FR_TOKEN
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition: strlcpy.c:38
fr_dict_attr_t const * da
Dictionary attribute defines the attribute.
Definition: pair.h:113
#define FIND_MODE_NAME
Definition: files.c:72
String of printable characters.
Definition: radius.h:33
FR_TOKEN fr_pair_list_afrom_str(TALLOC_CTX *ctx, char const *buffer, VALUE_PAIR **head)
Read one line of attribute/value pairs into a list.
Definition: pair.c:1261
PW_TYPE type
Value type.
Definition: dict.h:80
#define RCSID(id)
Definition: build.h:135
char * talloc_typed_strdup(void const *t, char const *p)
Call talloc strdup, setting the type on the new chunk correctly.
Definition: missing.c:588
VALUE_PAIR * reply
Definition: tmpl.h:122
#define ERROR(fmt,...)
Definition: log.h:145