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