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