6779d119035ecb39dbd9ec07f3b76b4312d5a93a
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  * Copyright 2000  The FreeRADIUS server project
19  */
20
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <freeradius-devel/autoconf.h>
25 #include <freeradius-devel/radiusd.h>
26 #include <freeradius-devel/modules.h>
27
28
29 struct mypasswd {
30         struct mypasswd *next;
31         char *listflag;
32         char *field[1];
33 };
34
35 struct hashtable {
36         int tablesize;
37         int keyfield;
38         int nfields;
39         int islist;
40         int ignorenis;
41         char * filename;
42         struct mypasswd **table;
43         struct mypasswd *last_found;
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(const char* buffer, int nfields, int* 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(const char* 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(const unsigned char * 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 == NULL) 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) free(ht->filename);
155         free(ht);
156 }
157
158 static struct hashtable * build_hash_table (const char * file, int nfields,
159         int keyfield, int islist, int tablesize, int ignorenis, char delimiter)
160 {
161 #define passwd ((struct mypasswd *) ht->buffer)
162         char buffer[1024];
163         struct hashtable* ht;
164         size_t len;
165         unsigned int h;
166         struct mypasswd *hashentry, *hashentry1;
167         char *list;
168         char *nextlist=0;
169         int i;
170
171         ht = (struct hashtable *) rad_malloc(sizeof(struct hashtable));
172         if(!ht) {
173                 return NULL;
174         }
175         memset(ht, 0, sizeof(struct hashtable));
176         ht->filename = strdup(file);
177         if(!ht->filename) {
178                 free(ht);
179                 return NULL;
180         }
181         ht->tablesize = tablesize;
182         ht->nfields = nfields;
183         ht->keyfield = keyfield;
184         ht->islist = islist;
185         ht->ignorenis = ignorenis;
186         if (delimiter) ht->delimiter = delimiter;
187         else ht->delimiter = ':';
188         if(!tablesize) return ht;
189         if(!(ht->fp = fopen(file,"r"))) return NULL;
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 {
248 #define passwd ((struct mypasswd *) ht->buffer)
249         struct mypasswd * hashentry;
250         char buffer[1024];
251         int len;
252         char *list, *nextlist;
253
254         if (ht->tablesize > 0) {
255                 /* get saved address of next item to check from buffer */
256                 hashentry = ht->last_found;
257                 for (; hashentry; hashentry = hashentry->next) {
258                         if (!strcmp(hashentry->field[ht->keyfield], name)) {
259                                 /* save new address */
260                                 ht->last_found = hashentry->next;
261                                 return hashentry;
262                         }
263                 }
264                 return NULL;
265         }
266         /*      printf("try to find in file\n"); */
267         if (!ht->fp) return NULL;
268         while (fgets(buffer, 1024,ht->fp)) {
269                 if(*buffer && *buffer!='\n' && (len = string_to_entry(buffer, ht->nfields, ht->delimiter, passwd, sizeof(ht->buffer)-1)) &&
270                         (!ht->ignorenis || (*buffer !='-' && *buffer != '+') ) ){
271                         if(!ht->islist) {
272                                 if(!strcmp(passwd->field[ht->keyfield], name))
273                                         return passwd;
274                         }
275                         else {
276                                 for (list = passwd->field[ht->keyfield], nextlist = list; nextlist; list = nextlist) {
277                                         for(nextlist = list; *nextlist && *nextlist!=','; nextlist++);
278                                         if(!*nextlist)nextlist = 0;
279                                         else *nextlist++ = 0;
280                                         if(!strcmp(list, name)) return passwd;
281                                 }
282                         }
283
284                 }
285         }
286         fclose(ht->fp);
287         ht->fp = NULL;
288         return NULL;
289 #undef passwd
290 }
291
292 static struct mypasswd * get_pw_nam(char * name, struct hashtable* ht)
293 {
294         int h;
295         struct mypasswd * hashentry;
296
297         if (!ht || !name || *name == '\0') return NULL;
298         ht->last_found = NULL;
299         if (ht->tablesize > 0) {
300                 h = hash (name, ht->tablesize);
301                 for (hashentry = ht->table[h]; hashentry; hashentry = hashentry->next)
302                         if (!strcmp(hashentry->field[ht->keyfield], name)){
303                                 /* save address of next item to check into buffer */
304                                 ht->last_found=hashentry->next;
305                                 return hashentry;
306                         }
307                 return NULL;
308         }
309         if (ht->fp) fclose(ht->fp);
310         if (!(ht->fp=fopen(ht->filename, "r"))) return NULL;
311         return get_next(name, ht);
312 }
313
314 #ifdef TEST
315
316 #define MALLOC_CHECK_ 1
317
318 int main(void){
319  struct hashtable *ht;
320  char *buffer;
321  struct mypasswd* pw;
322  int i;
323
324  ht = build_hash_table("/etc/group", 4, 3, 1, 100, 0, ":");
325  if(!ht) {
326         printf("Hash table not built\n");
327         return -1;
328  }
329  for (i=0; i<ht->tablesize; i++) if (ht->table[i]) {
330   printf("%d:\n", i);
331   for(pw=ht->table[i]; pw; pw=pw->next) printpw(pw, 4);
332  }
333
334  while(fgets(buffer, 1024, stdin)){
335   buffer[strlen(buffer)-1] = 0;
336   pw = get_pw_nam(buffer, ht);
337   printpw(pw,4);
338   while (pw = get_next(buffer, ht)) printpw(pw,4);
339  }
340  release_ht(ht);
341 }
342
343 #else  /* TEST */
344 struct passwd_instance {
345         struct hashtable *ht;
346         struct mypasswd *pwdfmt;
347         char *filename;
348         char *format;
349         char * delimiter;
350         int allowmultiple;
351         int ignorenislike;
352         int hashsize;
353         int nfields;
354         int keyfield;
355         int listable;
356         int keyattr;
357         int keyattrtype;
358         int ignoreempty;
359 };
360
361 static const CONF_PARSER module_config[] = {
362         { "filename",   PW_TYPE_FILENAME,
363            offsetof(struct passwd_instance, filename), NULL,  NULL },
364         { "format",   PW_TYPE_STRING_PTR,
365            offsetof(struct passwd_instance, format), NULL,  NULL },
366         { "delimiter",   PW_TYPE_STRING_PTR,
367            offsetof(struct passwd_instance, delimiter), NULL,  ":" },
368         { "ignorenislike",   PW_TYPE_BOOLEAN,
369            offsetof(struct passwd_instance, ignorenislike), NULL,  "yes" },
370         { "ignoreempty",   PW_TYPE_BOOLEAN,
371            offsetof(struct passwd_instance, ignoreempty), NULL,  "yes" },
372         { "allowmultiplekeys",   PW_TYPE_BOOLEAN,
373            offsetof(struct passwd_instance, allowmultiple), NULL,  "no" },
374         { "hashsize",   PW_TYPE_INTEGER,
375            offsetof(struct passwd_instance, hashsize), NULL,  "100" },
376         { NULL, -1, 0, NULL, NULL }
377 };
378
379 static int passwd_instantiate(CONF_SECTION *conf, void **instance)
380 {
381 #define inst ((struct passwd_instance *)*instance)
382         int nfields=0, keyfield=-1, listable=0;
383         char *s;
384         char *lf=NULL; /* destination list flags temporary */
385         int len;
386         int i;
387         DICT_ATTR * da;
388
389         *instance = rad_malloc(sizeof(struct passwd_instance));
390         if ( !*instance) {
391                 radlog(L_ERR, "rlm_passwd: cann't alloc instance");
392                 return -1;
393         }
394         memset(*instance, 0, sizeof(struct passwd_instance));
395         if (cf_section_parse(conf, inst, module_config) < 0) {
396                 free(inst);
397                 radlog(L_ERR, "rlm_passwd: cann't parse configuration");
398                 return -1;
399         }
400         if(!inst->filename || *inst->filename == '\0' || !inst->format || *inst->format == '\0') {
401                 radlog(L_ERR, "rlm_passwd: cann't find passwd file and/or format in configuration");
402                 return -1;
403         }
404         lf=strdup(inst->format);
405         if ( lf == NULL) {
406                 radlog(L_ERR, "rlm_passwd: memory allocation failed for lf");
407                 return -1;
408         }
409         memset(lf, 0, strlen(inst->format));
410         s = inst->format - 1;
411         do {
412                 if(s == inst->format - 1 || *s == ':'){
413                         if(*(s+1) == '*'){
414                                 keyfield = nfields;
415                                 s++;
416                         }
417                         if(*(s+1) == ','){
418                                 listable = 1;
419                                 s++;
420                         }
421                         if(*(s+1) == '='){
422                                 lf[nfields]=1;
423                                 s++;
424                         }
425                         if(*(s+1) == '~'){
426                                 lf[nfields]=2;
427                                 s++;
428                         }
429                         nfields++;
430                 }
431                 s++;
432         }while(*s);
433         if(keyfield < 0) {
434                 radlog(L_ERR, "rlm_passwd: no field market as key in format: %s", inst->format);
435                 return -1;
436         }
437         if (! (inst->ht = build_hash_table (inst->filename, nfields, keyfield, listable, inst->hashsize, inst->ignorenislike, *inst->delimiter)) ){
438                 radlog(L_ERR, "rlm_passwd: can't build hashtable from passwd file");
439                 return -1;
440         }
441         if (! (inst->pwdfmt = mypasswd_malloc(inst->format, nfields, &len)) ){
442                 radlog(L_ERR, "rlm_passwd: memory allocation failed");
443                 release_ht(inst->ht);
444                 return -1;
445         }
446         if (!string_to_entry(inst->format, nfields, ':', inst->pwdfmt , len)) {
447                 radlog(L_ERR, "rlm_passwd: unable to convert format entry");
448                 release_ht(inst->ht);
449                 return -1;
450         }
451
452         memcpy(inst->pwdfmt->listflag, lf, nfields);
453
454         free(lf);
455         for (i=0; i<nfields; i++) {
456                 if (*inst->pwdfmt->field[i] == '*') inst->pwdfmt->field[i]++;
457                 if (*inst->pwdfmt->field[i] == ',') inst->pwdfmt->field[i]++;
458                 if (*inst->pwdfmt->field[i] == '=') inst->pwdfmt->field[i]++;
459                 if (*inst->pwdfmt->field[i] == '~') inst->pwdfmt->field[i]++;
460         }
461         if (!*inst->pwdfmt->field[keyfield]) {
462                 radlog(L_ERR, "rlm_passwd: key field is empty");
463                 release_ht(inst->ht);
464                 return -1;
465         }
466         if (! (da = dict_attrbyname (inst->pwdfmt->field[keyfield])) ) {
467                 radlog(L_ERR, "rlm_passwd: unable to resolve attribute: %s", inst->pwdfmt->field[keyfield]);
468                 release_ht(inst->ht);
469                 return -1;
470         }
471         inst->keyattr = da->attr;
472         inst->keyattrtype = da->type;
473         inst->nfields = nfields;
474         inst->keyfield = keyfield;
475         inst->listable = listable;
476         DEBUG("rlm_passwd: nfields: %d keyfield %d(%s) listable: %s", nfields, keyfield, inst->pwdfmt->field[keyfield], listable?"yes":"no");
477         return 0;
478
479 #undef inst
480 }
481
482 static int passwd_detach (void *instance) {
483 #define inst ((struct passwd_instance *)instance)
484         if(inst->ht) release_ht(inst->ht);
485         if (inst->filename != NULL)     free(inst->filename);
486         if (inst->format != NULL)       free(inst->format);
487         if (inst->delimiter != NULL)    free(inst->delimiter);
488         free(instance);
489         return 0;
490 #undef inst
491 }
492
493 static void addresult (struct passwd_instance * inst, VALUE_PAIR ** vp, struct mypasswd * pw, char when, const char *listname)
494 {
495         int i;
496         VALUE_PAIR *newpair;
497
498         for (i=0; i<inst->nfields; i++) {
499                 if (inst->pwdfmt->field[i] && *inst->pwdfmt->field[i] && pw->field[i] && i != inst->keyfield  && inst->pwdfmt->listflag[i] == when) {
500                         if ( !inst->ignoreempty || pw->field[i][0] != 0 ) { /* if value in key/value pair is not empty */
501                                 if (! (newpair = pairmake (inst->pwdfmt->field[i], pw->field[i], T_OP_EQ))) {
502                                         radlog(L_AUTH, "rlm_passwd: Unable to create %s: %s", inst->pwdfmt->field[i], pw->field[i]);
503                                         return;
504                                 }
505                                 radlog(L_DBG, "rlm_passwd: Added %s: '%s' to %s ", inst->pwdfmt->field[i], pw->field[i], listname);
506                                 pairadd (vp, newpair);
507                         } else
508                                 radlog(L_DBG, "rlm_passwd: NOOP %s: '%s' to %s ", inst->pwdfmt->field[i], pw->field[i], listname);
509                 }
510         }
511 }
512
513 static int passwd_authorize(void *instance, REQUEST *request)
514 {
515 #define inst ((struct passwd_instance *)instance)
516         char * name;
517         char buffer[1024];
518         VALUE_PAIR * key;
519         struct mypasswd * pw;
520         int found = 0;
521
522         if(!request || !request->packet ||!request->packet->vps)
523          return RLM_MODULE_INVALID;
524         for (key = request->packet->vps;
525          key && (key = pairfind (key, inst->keyattr));
526           key = key->next ){
527                 switch (inst->keyattrtype) {
528                         case PW_TYPE_INTEGER:
529                                 snprintf(buffer, 1024, "%u", key->lvalue);
530                                 name = buffer;
531                                 break;
532                         default:
533                                 name = key->vp_strvalue;
534                 }
535                 if (! (pw = get_pw_nam(name, inst->ht)) ) {
536                         continue;
537                 }
538                 do {
539                         addresult(inst, &request->config_items, pw, 0, "config_items");
540                         addresult(inst, &request->reply->vps, pw, 1, "reply_items");
541                         addresult(inst, &request->packet->vps,  pw, 2, "request_items");
542                 } while ( (pw = get_next(name, inst->ht)) );
543                 found++;
544                 if (!inst->allowmultiple) break;
545         }
546         if(!found) {
547                 return RLM_MODULE_NOTFOUND;
548         }
549         return RLM_MODULE_OK;
550
551 #undef inst
552 }
553
554 module_t rlm_passwd = {
555         RLM_MODULE_INIT,
556         "passwd",
557         RLM_TYPE_THREAD_SAFE,           /* type */
558         passwd_instantiate,             /* instantiation */
559         passwd_detach,                  /* detach */
560         {
561                 NULL,                   /* authentication */
562                 passwd_authorize,       /* authorization */
563                 NULL,                   /* pre-accounting */
564                 NULL,                   /* accounting */
565                 NULL,                   /* checksimul */
566                 NULL,                   /* pre-proxy */
567                 NULL,                   /* post-proxy */
568                 NULL                    /* post-auth */
569         },
570 };
571 #endif /* TEST */