Massively cleaned up #include's, so they're in a consistent
[freeradius.git] / src / modules / rlm_passwd / rlm_passwd.c
1 /*
2  * rlm_passwd.c
3  *
4  *   This program is free software; you can redistribute it and/or modify
5  *   it under the terms of the GNU General Public License as published by
6  *   the Free Software Foundation; either version 2 of the License, or
7  *   (at your option) any later version.
8  *
9  *   This program is distributed in the hope that it will be useful,
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *   GNU General Public License for more details.
13  *
14  *   You should have received a copy of the GNU General Public License
15  *   along with this program; if not, write to the Free Software
16  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  *
18  * Copyright 2000,2006  The FreeRADIUS server project
19  */
20
21 #include <freeradius-devel/ident.h>
22 RCSID("$Id$")
23
24 #include <freeradius-devel/radiusd.h>
25 #include <freeradius-devel/modules.h>
26
27 struct mypasswd {
28         struct mypasswd *next;
29         char *listflag;
30         char *field[1];
31 };
32
33 struct hashtable {
34         int tablesize;
35         int keyfield;
36         int nfields;
37         int islist;
38         int ignorenis;
39         char * filename;
40         struct mypasswd **table;
41         struct mypasswd *last_found;
42         char buffer[1024];
43         FILE *fp;
44         char delimiter;
45 };
46
47
48 #ifdef TEST
49
50 #define rad_malloc(s) malloc(s)
51
52 void printpw(struct mypasswd *pw, int nfields){
53   int i;
54   if (pw) {
55         for( i = 0; i < nfields; i++ ) printf("%s:", pw->field[i]);
56         printf("\n");
57   }
58   else printf ("Not found\n");
59   fflush(stdout);
60 }
61 #endif
62
63
64 static struct mypasswd * mypasswd_malloc(const char* buffer, int nfields, int* len)
65 {
66         struct mypasswd *t;
67         /* reserve memory for (struct mypasswd) + listflag (nfields * sizeof (char*)) +
68         ** fields (nfields * sizeof (char)) + strlen (inst->format) + 1 */
69
70         *len=sizeof (struct mypasswd) + nfields * sizeof (char*) + nfields * sizeof (char ) + strlen(buffer) + 1;
71         t = (struct mypasswd *) rad_malloc(*len);
72         if (t) memset(t, 0, *len);
73         return (t);
74 }
75
76 static int string_to_entry(const char* string, int nfields, char delimiter,
77         struct mypasswd *passwd, size_t bufferlen)
78 {
79         char *str;
80         size_t len, i;
81         int fn=0;
82         char *data_beg;
83
84
85         len = strlen(string);
86         if(!len) return 0;
87         if (string[len-1] == '\n') len--;
88         if(!len) return 0;
89         if (string[len-1] == '\r') len--;
90         if(!len) return 0;
91         if (!len || !passwd ||
92             bufferlen < (len + nfields * sizeof (char*) + nfields * sizeof (char) + sizeof (struct mypasswd) + 1) ) return 0;
93         passwd->next = NULL;
94         data_beg=(char *)passwd + sizeof(struct mypasswd);
95         str = data_beg + nfields * sizeof (char) + nfields * sizeof (char*);
96         memcpy (str, string, len);
97         str[len] = 0;
98         passwd->field[fn++] = str;
99         passwd->listflag = data_beg + nfields * sizeof (char *);
100         for(i=0; i < len; i++){
101                 if (str[i] == delimiter) {
102                         str[i] = 0;
103                         passwd->field[fn++] = str + i + 1;
104                         if (fn == nfields) break;
105                 }
106         }
107         for (; fn < nfields; fn++) passwd->field[fn] = NULL;
108         return len + nfields * sizeof (char) + nfields * sizeof (char*) + sizeof (struct mypasswd) + 1;
109 }
110
111
112 static void destroy_password (struct mypasswd * pass)
113 {
114         struct mypasswd *p;
115         while ((p=pass)!=NULL) {
116                 pass = pass->next;
117                 free(p);
118         }
119 }
120
121
122 static unsigned int hash(const unsigned char * username, unsigned int tablesize)
123 {
124         int h=1;
125         while (*username) {
126                 h = h * 7907 + *username++;
127         }
128         return h%tablesize;
129 }
130
131 static void release_hash_table(struct hashtable * ht){
132         int i;
133
134         if (ht == NULL) return;
135         for (i = 0; i < ht->tablesize; i++)
136                 if (ht->table[i])
137                         destroy_password(ht->table[i]);
138         if (ht->table) {
139                 free(ht->table);
140                 ht->table = NULL;
141         }
142         if (ht->fp) {
143                 fclose(ht->fp);
144                 ht->fp = NULL;
145         }
146         ht->tablesize = 0;
147 }
148
149 static void release_ht(struct hashtable * ht){
150         if (!ht) return;
151         release_hash_table(ht);
152         if (ht->filename) free(ht->filename);
153         free(ht);
154 }
155
156 static struct hashtable * build_hash_table (const char * file, int nfields,
157         int keyfield, int islist, int tablesize, int ignorenis, char delimiter)
158 {
159 #define passwd ((struct mypasswd *) ht->buffer)
160         char buffer[1024];
161         struct hashtable* ht;
162         size_t len;
163         unsigned int h;
164         struct mypasswd *hashentry, *hashentry1;
165         char *list;
166         char *nextlist=0;
167         int i;
168
169         ht = (struct hashtable *) rad_malloc(sizeof(struct hashtable));
170         if(!ht) {
171                 return NULL;
172         }
173         memset(ht, 0, sizeof(struct hashtable));
174         ht->filename = strdup(file);
175         if(!ht->filename) {
176                 free(ht);
177                 return NULL;
178         }
179         ht->tablesize = tablesize;
180         ht->nfields = nfields;
181         ht->keyfield = keyfield;
182         ht->islist = islist;
183         ht->ignorenis = ignorenis;
184         if (delimiter) ht->delimiter = delimiter;
185         else ht->delimiter = ':';
186         if(!tablesize) return ht;
187         if(!(ht->fp = fopen(file,"r"))) return NULL;
188         memset(ht->buffer, 0, 1024);
189         ht->table = (struct mypasswd **) rad_malloc (tablesize * sizeof(struct mypasswd *));
190         if (!ht->table) {
191                 /*
192                  * Unable allocate memory for hash table
193                  * Still work without it
194                  */
195                 ht->tablesize = 0;
196                 return ht;
197         }
198         memset(ht->table, 0, tablesize * sizeof(struct mypasswd *));
199         while (fgets(buffer, 1024, ht->fp)) {
200                 if(*buffer && *buffer!='\n' && (!ignorenis || (*buffer != '+' && *buffer != '-')) ){
201                         if(!(hashentry = mypasswd_malloc(buffer, nfields, &len))){
202                                 release_hash_table(ht);
203                                 return ht;
204                         }
205                         len = string_to_entry(buffer, nfields, ht->delimiter, hashentry, len);
206                         if(!hashentry->field[keyfield] || *hashentry->field[keyfield] == '\0') {
207                                 free(hashentry);
208                                 continue;
209                         }
210
211                         if (islist) {
212                                 list = hashentry->field[keyfield];
213                                 for (nextlist = list; *nextlist && *nextlist!=','; nextlist++);
214                                 if (*nextlist) *nextlist++ = 0;
215                                 else nextlist = 0;
216                         }
217                         h = hash(hashentry->field[keyfield], tablesize);
218                         hashentry->next = ht->table[h];
219                         ht->table[h] = hashentry;
220                         if (islist) {
221                                 for(list=nextlist; nextlist; list = nextlist){
222                                         for (nextlist = list; *nextlist && *nextlist!=','; nextlist++);
223                                         if (*nextlist) *nextlist++ = 0;
224                                         else nextlist = 0;
225                                         if(!(hashentry1 = mypasswd_malloc("", nfields, &len))){
226                                                 release_hash_table(ht);
227                                                 return ht;
228                                         }
229                                         for (i=0; i<nfields; i++) hashentry1->field[i] = hashentry->field[i];
230                                         hashentry1->field[keyfield] = list;
231                                         h = hash(list, tablesize);
232                                         hashentry1->next = ht->table[h];
233                                         ht->table[h] = hashentry1;
234                                 }
235                         }
236                 }
237         }
238         fclose(ht->fp);
239         ht->fp = NULL;
240         return ht;
241 #undef passwd
242 }
243
244 static struct mypasswd * get_next(char *name, struct hashtable *ht)
245 {
246 #define passwd ((struct mypasswd *) ht->buffer)
247         struct mypasswd * hashentry;
248         char buffer[1024];
249         int len;
250         char *list, *nextlist;
251
252         if (ht->tablesize > 0) {
253                 /* get saved address of next item to check from buffer */
254                 hashentry = ht->last_found;
255                 for (; hashentry; hashentry = hashentry->next) {
256                         if (!strcmp(hashentry->field[ht->keyfield], name)) {
257                                 /* save new address */
258                                 ht->last_found = hashentry->next;
259                                 return hashentry;
260                         }
261                 }
262                 return NULL;
263         }
264         /*      printf("try to find in file\n"); */
265         if (!ht->fp) return NULL;
266         while (fgets(buffer, 1024,ht->fp)) {
267                 if(*buffer && *buffer!='\n' && (len = string_to_entry(buffer, ht->nfields, ht->delimiter, passwd, sizeof(ht->buffer)-1)) &&
268                         (!ht->ignorenis || (*buffer !='-' && *buffer != '+') ) ){
269                         if(!ht->islist) {
270                                 if(!strcmp(passwd->field[ht->keyfield], name))
271                                         return passwd;
272                         }
273                         else {
274                                 for (list = passwd->field[ht->keyfield], nextlist = list; nextlist; list = nextlist) {
275                                         for(nextlist = list; *nextlist && *nextlist!=','; nextlist++);
276                                         if(!*nextlist)nextlist = 0;
277                                         else *nextlist++ = 0;
278                                         if(!strcmp(list, name)) return passwd;
279                                 }
280                         }
281
282                 }
283         }
284         fclose(ht->fp);
285         ht->fp = NULL;
286         return NULL;
287 #undef passwd
288 }
289
290 static struct mypasswd * get_pw_nam(char * name, struct hashtable* ht)
291 {
292         int h;
293         struct mypasswd * hashentry;
294
295         if (!ht || !name || *name == '\0') return NULL;
296         ht->last_found = NULL;
297         if (ht->tablesize > 0) {
298                 h = hash (name, ht->tablesize);
299                 for (hashentry = ht->table[h]; hashentry; hashentry = hashentry->next)
300                         if (!strcmp(hashentry->field[ht->keyfield], name)){
301                                 /* save address of next item to check into buffer */
302                                 ht->last_found=hashentry->next;
303                                 return hashentry;
304                         }
305                 return NULL;
306         }
307         if (ht->fp) fclose(ht->fp);
308         if (!(ht->fp=fopen(ht->filename, "r"))) return NULL;
309         return get_next(name, ht);
310 }
311
312 #ifdef TEST
313
314 #define MALLOC_CHECK_ 1
315
316 int main(void){
317  struct hashtable *ht;
318  char *buffer;
319  struct mypasswd* pw;
320  int i;
321
322  ht = build_hash_table("/etc/group", 4, 3, 1, 100, 0, ":");
323  if(!ht) {
324         printf("Hash table not built\n");
325         return -1;
326  }
327  for (i=0; i<ht->tablesize; i++) if (ht->table[i]) {
328   printf("%d:\n", i);
329   for(pw=ht->table[i]; pw; pw=pw->next) printpw(pw, 4);
330  }
331
332  while(fgets(buffer, 1024, stdin)){
333   buffer[strlen(buffer)-1] = 0;
334   pw = get_pw_nam(buffer, ht);
335   printpw(pw,4);
336   while (pw = get_next(buffer, ht)) printpw(pw,4);
337  }
338  release_ht(ht);
339 }
340
341 #else  /* TEST */
342 struct passwd_instance {
343         struct hashtable *ht;
344         struct mypasswd *pwdfmt;
345         char *filename;
346         char *format;
347         char * delimiter;
348         int allowmultiple;
349         int ignorenislike;
350         int hashsize;
351         int nfields;
352         int keyfield;
353         int listable;
354         int keyattr;
355         int keyattrtype;
356         int ignoreempty;
357 };
358
359 static const CONF_PARSER module_config[] = {
360         { "filename",   PW_TYPE_FILENAME,
361            offsetof(struct passwd_instance, filename), NULL,  NULL },
362         { "format",   PW_TYPE_STRING_PTR,
363            offsetof(struct passwd_instance, format), NULL,  NULL },
364         { "delimiter",   PW_TYPE_STRING_PTR,
365            offsetof(struct passwd_instance, delimiter), NULL,  ":" },
366         { "ignorenislike",   PW_TYPE_BOOLEAN,
367            offsetof(struct passwd_instance, ignorenislike), NULL,  "yes" },
368         { "ignoreempty",   PW_TYPE_BOOLEAN,
369            offsetof(struct passwd_instance, ignoreempty), NULL,  "yes" },
370         { "allowmultiplekeys",   PW_TYPE_BOOLEAN,
371            offsetof(struct passwd_instance, allowmultiple), NULL,  "no" },
372         { "hashsize",   PW_TYPE_INTEGER,
373            offsetof(struct passwd_instance, hashsize), NULL,  "100" },
374         { NULL, -1, 0, NULL, NULL }
375 };
376
377 static int passwd_instantiate(CONF_SECTION *conf, void **instance)
378 {
379 #define inst ((struct passwd_instance *)*instance)
380         int nfields=0, keyfield=-1, listable=0;
381         char *s;
382         char *lf=NULL; /* destination list flags temporary */
383         int len;
384         int i;
385         DICT_ATTR * da;
386
387         *instance = rad_malloc(sizeof(struct passwd_instance));
388         if ( !*instance) {
389                 radlog(L_ERR, "rlm_passwd: cann't alloc instance");
390                 return -1;
391         }
392         memset(*instance, 0, sizeof(struct passwd_instance));
393         if (cf_section_parse(conf, inst, module_config) < 0) {
394                 free(inst);
395                 radlog(L_ERR, "rlm_passwd: cann't parse configuration");
396                 return -1;
397         }
398         if(!inst->filename || *inst->filename == '\0' || !inst->format || *inst->format == '\0') {
399                 radlog(L_ERR, "rlm_passwd: cann't find passwd file and/or format in configuration");
400                 return -1;
401         }
402         lf=strdup(inst->format);
403         if ( lf == NULL) {
404                 radlog(L_ERR, "rlm_passwd: memory allocation failed for lf");
405                 return -1;
406         }
407         memset(lf, 0, strlen(inst->format));
408         s = inst->format - 1;
409         do {
410                 if(s == inst->format - 1 || *s == ':'){
411                         if(*(s+1) == '*'){
412                                 keyfield = nfields;
413                                 s++;
414                         }
415                         if(*(s+1) == ','){
416                                 listable = 1;
417                                 s++;
418                         }
419                         if(*(s+1) == '='){
420                                 lf[nfields]=1;
421                                 s++;
422                         }
423                         if(*(s+1) == '~'){
424                                 lf[nfields]=2;
425                                 s++;
426                         }
427                         nfields++;
428                 }
429                 s++;
430         }while(*s);
431         if(keyfield < 0) {
432                 radlog(L_ERR, "rlm_passwd: no field market as key in format: %s", inst->format);
433                 return -1;
434         }
435         if (! (inst->ht = build_hash_table (inst->filename, nfields, keyfield, listable, inst->hashsize, inst->ignorenislike, *inst->delimiter)) ){
436                 radlog(L_ERR, "rlm_passwd: can't build hashtable from passwd file");
437                 return -1;
438         }
439         if (! (inst->pwdfmt = mypasswd_malloc(inst->format, nfields, &len)) ){
440                 radlog(L_ERR, "rlm_passwd: memory allocation failed");
441                 release_ht(inst->ht);
442                 return -1;
443         }
444         if (!string_to_entry(inst->format, nfields, ':', inst->pwdfmt , len)) {
445                 radlog(L_ERR, "rlm_passwd: unable to convert format entry");
446                 release_ht(inst->ht);
447                 return -1;
448         }
449
450         memcpy(inst->pwdfmt->listflag, lf, nfields);
451
452         free(lf);
453         for (i=0; i<nfields; i++) {
454                 if (*inst->pwdfmt->field[i] == '*') inst->pwdfmt->field[i]++;
455                 if (*inst->pwdfmt->field[i] == ',') inst->pwdfmt->field[i]++;
456                 if (*inst->pwdfmt->field[i] == '=') inst->pwdfmt->field[i]++;
457                 if (*inst->pwdfmt->field[i] == '~') inst->pwdfmt->field[i]++;
458         }
459         if (!*inst->pwdfmt->field[keyfield]) {
460                 radlog(L_ERR, "rlm_passwd: key field is empty");
461                 release_ht(inst->ht);
462                 return -1;
463         }
464         if (! (da = dict_attrbyname (inst->pwdfmt->field[keyfield])) ) {
465                 radlog(L_ERR, "rlm_passwd: unable to resolve attribute: %s", inst->pwdfmt->field[keyfield]);
466                 release_ht(inst->ht);
467                 return -1;
468         }
469         inst->keyattr = da->attr;
470         inst->keyattrtype = da->type;
471         inst->nfields = nfields;
472         inst->keyfield = keyfield;
473         inst->listable = listable;
474         DEBUG("rlm_passwd: nfields: %d keyfield %d(%s) listable: %s", nfields, keyfield, inst->pwdfmt->field[keyfield], listable?"yes":"no");
475         return 0;
476
477 #undef inst
478 }
479
480 static int passwd_detach (void *instance) {
481 #define inst ((struct passwd_instance *)instance)
482         if(inst->ht) release_ht(inst->ht);
483         free(instance);
484         return 0;
485 #undef inst
486 }
487
488 static void addresult (struct passwd_instance * inst, VALUE_PAIR ** vp, struct mypasswd * pw, char when, const char *listname)
489 {
490         int i;
491         VALUE_PAIR *newpair;
492
493         for (i=0; i<inst->nfields; i++) {
494                 if (inst->pwdfmt->field[i] && *inst->pwdfmt->field[i] && pw->field[i] && i != inst->keyfield  && inst->pwdfmt->listflag[i] == when) {
495                         if ( !inst->ignoreempty || pw->field[i][0] != 0 ) { /* if value in key/value pair is not empty */
496                                 if (! (newpair = pairmake (inst->pwdfmt->field[i], pw->field[i], T_OP_EQ))) {
497                                         radlog(L_AUTH, "rlm_passwd: Unable to create %s: %s", inst->pwdfmt->field[i], pw->field[i]);
498                                         return;
499                                 }
500                                 radlog(L_DBG, "rlm_passwd: Added %s: '%s' to %s ", inst->pwdfmt->field[i], pw->field[i], listname);
501                                 pairadd (vp, newpair);
502                         } else
503                                 radlog(L_DBG, "rlm_passwd: NOOP %s: '%s' to %s ", inst->pwdfmt->field[i], pw->field[i], listname);
504                 }
505         }
506 }
507
508 static int passwd_authorize(void *instance, REQUEST *request)
509 {
510 #define inst ((struct passwd_instance *)instance)
511         char * name;
512         char buffer[1024];
513         VALUE_PAIR * key;
514         struct mypasswd * pw;
515         int found = 0;
516
517         if(!request || !request->packet ||!request->packet->vps)
518          return RLM_MODULE_INVALID;
519         for (key = request->packet->vps;
520          key && (key = pairfind (key, inst->keyattr));
521           key = key->next ){
522                 switch (inst->keyattrtype) {
523                         case PW_TYPE_INTEGER:
524                                 snprintf(buffer, 1024, "%u", key->lvalue);
525                                 name = buffer;
526                                 break;
527                         default:
528                                 name = key->vp_strvalue;
529                 }
530                 if (! (pw = get_pw_nam(name, inst->ht)) ) {
531                         continue;
532                 }
533                 do {
534                         addresult(inst, &request->config_items, pw, 0, "config_items");
535                         addresult(inst, &request->reply->vps, pw, 1, "reply_items");
536                         addresult(inst, &request->packet->vps,  pw, 2, "request_items");
537                 } while ( (pw = get_next(name, inst->ht)) );
538                 found++;
539                 if (!inst->allowmultiple) break;
540         }
541         if(!found) {
542                 return RLM_MODULE_NOTFOUND;
543         }
544         return RLM_MODULE_OK;
545
546 #undef inst
547 }
548
549 module_t rlm_passwd = {
550         RLM_MODULE_INIT,
551         "passwd",
552         RLM_TYPE_THREAD_SAFE,           /* type */
553         passwd_instantiate,             /* instantiation */
554         passwd_detach,                  /* detach */
555         {
556                 NULL,                   /* authentication */
557                 passwd_authorize,       /* authorization */
558                 NULL,                   /* pre-accounting */
559                 NULL,                   /* accounting */
560                 NULL,                   /* checksimul */
561                 NULL,                   /* pre-proxy */
562                 NULL,                   /* post-proxy */
563                 NULL                    /* post-auth */
564         },
565 };
566 #endif /* TEST */