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