End the list of configuration parameters.
[freeradius.git] / src / modules / rlm_passwd / rlm_passwd.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 /*#include "autoconf.h"
5 #include "libradius.h"*/
6 #include "radiusd.h"
7 #include "modules.h"
8
9
10 struct mypasswd {
11         struct mypasswd *next;
12         char *field[0];
13         char data[1];
14 };
15
16 struct hashtable {
17         int tablesize;
18         int keyfield;
19         int nfields;
20         int islist;
21         int ignorenis;
22         char * filename;
23         struct mypasswd **table;
24         char buffer[1024];
25         FILE *fp;
26         char *delimiter;
27 };
28
29
30 #ifdef TEST
31 void printpw(struct mypasswd *pw, int nfields){
32   int i;
33   if (pw) {
34         for( i = 0; i < nfields; i++ ) printf("%s:", pw->field[i]);
35         printf("\n");
36   }
37   else printf ("Not found\n");
38   fflush(stdout);
39 }
40 #endif
41
42 static int string_to_entry(const char* string, int nfields, char delimiter,
43         struct mypasswd *passwd, int bufferlen)
44 {
45         char *str;
46         int len, i, fn=0;
47         
48
49         len = strlen(string);
50         if(!len) return 0;
51         if (string[len-1] == '\n') len--;
52         if(!len) return 0;
53         if (string[len-1] == '\r') len--;
54         if(!len) return 0;
55         if (!len || !passwd || bufferlen < (len + nfields * sizeof (char*) + sizeof (struct mypasswd) + 1) ) return 0;
56         passwd->next = 0;
57         str = passwd->data + nfields * sizeof (char *);
58         memcpy (str, string, len);
59         str[len] = 0;
60         passwd->field[fn++] = str;
61         for(i=0; i < len; i++){
62                 if (str[i] == delimiter) {
63                         str[i] = 0;
64                         passwd->field[fn++] = str + i + 1;
65                         if (fn == nfields) break;
66                 }
67         }
68         for (; fn < nfields; fn++) passwd->field[fn] = NULL;
69 /*
70 printpw(passwd, 7);
71 */
72         return len + nfields * sizeof (char*) + sizeof (struct mypasswd) + 1;
73 }
74
75
76 static void destroy_password (struct mypasswd * pass)
77 {
78         if(!pass) return;
79         if(pass->next)destroy_password(pass->next);
80         free(pass);
81 }
82
83
84 static unsigned int hash(const unsigned char * username, unsigned int tablesize)
85 {
86         int h=1;
87         while (*username) {
88                 h = h * 7907 + *username++;
89         }
90         return h%tablesize;
91
92
93 static void release_hash_table(struct hashtable * ht){
94         int i;
95
96         if (!ht) return;
97         for (i=0; i<ht->tablesize; i++) 
98                 if (ht->table[i])
99                         destroy_password(ht->table[i]);
100         if (ht->table) free(ht->table);
101         if (ht->filename) free(ht->filename);
102         if (ht->fp) fclose(ht->fp);
103         free(ht);
104 }
105
106 static struct hashtable * build_hash_table (const char * file, int nfields,
107         int keyfield, int islist, int tablesize, int ignorenis, char * delimiter)
108 {
109 #define passwd ((struct mypasswd *) ht->buffer)
110         char buffer[1024];
111         struct hashtable* ht;
112         int len;
113         int h;
114         struct mypasswd *hashentry, *hashentry1;
115         char *list;
116         char *nextlist=0;
117         int i;
118         
119         ht = (struct hashtable *) malloc(sizeof(struct hashtable));
120         if(!ht) {
121                 return NULL;
122         }
123         ht->filename = strdup(file);
124         if(!ht->filename) {
125                 free(ht);
126                 return NULL;
127         }
128         ht->tablesize = tablesize;
129         ht->nfields = nfields;
130         ht->keyfield = keyfield;
131         ht->islist = islist;
132         ht->ignorenis = ignorenis;
133         if (delimiter && *delimiter) ht->delimiter = delimiter;
134         else ht->delimiter = ":";
135         if(!tablesize) return ht;
136         if(!(ht->fp = fopen(file,"r"))) return NULL;
137         memset(ht->buffer, 0, 1024);
138         ht->table = (struct mypasswd **) malloc (tablesize * sizeof(struct mypasswd *));
139         if (!ht->table) {
140                 /*
141                  * Unable allocate memory for hash table
142                  * Still work without it
143                  */
144                 ht->tablesize = 0;
145                 return ht;
146         }
147         while (fgets(buffer, 1024, ht->fp)) {
148                 if(*buffer && *buffer!='\n' && (!ignorenis || (*buffer != '+' && *buffer != '-')) ){
149                         len = strlen(buffer) + nfields * sizeof (char *) + sizeof (struct mypasswd) + 1;
150                         if(!(hashentry = (struct mypasswd *) malloc(len))){
151                                 release_hash_table(ht);
152                                 ht->tablesize = 0;
153                                 return ht;
154                         }
155                         len = string_to_entry(buffer, nfields, *ht->delimiter, hashentry, len);
156                         if(!hashentry->field[keyfield] || *hashentry->field[keyfield] == '\0') {
157                                 free(hashentry);
158                                 continue;
159                         }
160                         
161                         if (islist) {
162                                 list = hashentry->field[keyfield];
163                                 for (nextlist = list; *nextlist && *nextlist!=','; nextlist++);
164                                 if (*nextlist) *nextlist++ = 0;
165                                 else nextlist = 0;
166                         }
167                         h = hash(hashentry->field[keyfield], tablesize);
168                         hashentry->next = ht->table[h];
169                         ht->table[h] = hashentry;
170                         if (islist) {
171                                 for(list=nextlist; nextlist; list = nextlist){
172                                         for (nextlist = list; *nextlist && *nextlist!=','; nextlist++);
173                                         if (*nextlist) *nextlist++ = 0;
174                                         else nextlist = 0;
175                                         if(!(hashentry1 = (struct mypasswd *) malloc(sizeof(struct mypasswd) + nfields * sizeof(char *)))){
176                                                 release_hash_table(ht);
177                                                 ht->tablesize = 0;
178                                                 return ht;
179                                         }
180                                         for (i=0; i<nfields; i++) hashentry1->field[i] = hashentry->field[i];
181                                         hashentry1->field[keyfield] = list;
182                                         h = hash(list, tablesize);
183                                         hashentry1->next = ht->table[h];
184                                         ht->table[h] = hashentry1;
185                                 }
186                         }
187                 }
188         }
189         fclose(ht->fp);
190         ht->fp = NULL;
191         return ht;
192 #undef passwd
193 }
194
195 static struct mypasswd * get_next(char *name, struct hashtable *ht)
196 {
197 #define passwd ((struct mypasswd *) ht->buffer)
198         struct mypasswd * hashentry;
199         char buffer[1024];
200         int len;
201         char *list, *nextlist;
202         
203         if (ht->tablesize > 0) {
204                 /* get saved address of next item to check from buffer */
205                 memcpy(&hashentry, ht->buffer, sizeof(hashentry));
206                 for (; hashentry; hashentry = hashentry->next) {
207                         if (!strcmp(hashentry->field[ht->keyfield], name)) {
208                                 /* save new address */
209                                 memcpy(ht->buffer, &hashentry->next, sizeof(hashentry));
210                                 return hashentry;
211                         }
212                 }
213                 memset(ht->buffer, 0, sizeof(hashentry));
214                 return NULL;
215         }
216         if (!ht->fp) return NULL;
217         while (fgets(buffer, 1024,ht->fp)) {
218                 if(*buffer && *buffer!='\n' && (len = string_to_entry(buffer, ht->nfields, *ht->delimiter, passwd, 1024)) &&
219                         (!ht->ignorenis || (*buffer !='-' && *buffer != '+') ) ){
220                         if(!ht->islist) {
221                                 if(!strcmp(passwd->field[ht->keyfield], name))
222                                         return passwd;
223                         }
224                         else {
225                                 for (list = passwd->field[ht->keyfield], nextlist = list; nextlist; list = nextlist) {
226                                         for(nextlist = list; *nextlist && *nextlist!=','; nextlist++);
227                                         if(!*nextlist)nextlist = 0;
228                                         else *nextlist++ = 0;
229                                         if(!strcmp(list, name)) return passwd;
230                                 }
231                         }
232                         
233                 }
234         }
235         fclose(ht->fp);
236         ht->fp = NULL;
237         return NULL;
238 #undef passwd
239 }
240
241 static struct mypasswd * get_pw_nam(char * name, struct hashtable* ht)
242 {
243         int h;
244         struct mypasswd * hashentry;
245         
246         if (!ht || !name || *name == '\0') return NULL;
247         if (ht->tablesize > 0) {
248                 h = hash (name, ht->tablesize);
249                 for (hashentry = ht->table[h]; hashentry; hashentry = hashentry->next)
250                         if (!strcmp(hashentry->field[ht->keyfield], name)){
251                                 /* save address of next item to check into buffer */
252                                 memcpy(ht->buffer, &hashentry->next, sizeof(hashentry));
253                                 return hashentry;
254                         }
255                 memset(ht->buffer, 0, sizeof(hashentry));
256                 return NULL;
257         }
258         if (ht->fp) fclose(ht->fp);
259         if (!(ht->fp=fopen(ht->filename, "r"))) return NULL;
260         return get_next(name, ht);
261 }
262
263 #ifdef TEST
264
265 int main(void){
266  struct hashtable *ht;
267  char *buffer;
268  struct mypasswd* pw;
269  int i;
270  
271  ht = build_hash_table("/etc/group", 4, 3, 1, 100, 0);
272  if(!ht) {
273         printf("Hash table not built\n");
274         return -1;
275  }
276  
277  for (i=0; i<ht->tablesize; i++) if (ht->table[i]) {
278   printf("%d:\n", i);
279   for(pw=ht->table[i]; pw; pw=pw->next) printpw(pw, 4);
280  }
281  while(fgets(buffer, 1024, stdin)){
282   buffer[strlen(buffer)-1] = 0;
283   pw = get_pw_nam(buffer, ht);
284   printpw(pw,4);
285   while (pw = get_next(buffer, ht))printpw(pw,4);
286  }
287 }
288
289 #else  /* TEST */
290 struct passwd_instance {
291         struct hashtable *ht;
292         struct mypasswd *pwdfmt;
293         char *filename;
294         char *format;
295         char *authtype;
296         char * delimiter;
297         int allowmultiple;
298         int ignorenislike;
299         int hashsize;
300         int nfields;
301         int keyfield;
302         int listable;
303         int keyattr;
304         int keyattrtype;
305 };
306
307 static CONF_PARSER module_config[] = {
308         { "filename",   PW_TYPE_STRING_PTR,
309            offsetof(struct passwd_instance, filename), NULL,  NULL },   
310         { "format",   PW_TYPE_STRING_PTR,
311            offsetof(struct passwd_instance, format), NULL,  NULL },
312         { "authtype",   PW_TYPE_STRING_PTR,
313            offsetof(struct passwd_instance, authtype), NULL,  NULL },
314         { "delimiter",   PW_TYPE_STRING_PTR,
315            offsetof(struct passwd_instance, delimiter), NULL,  ":" },
316         { "ignorenislike",   PW_TYPE_BOOLEAN,
317            offsetof(struct passwd_instance, ignorenislike), NULL,  "yes" },
318         { "allowmultiplekeys",   PW_TYPE_BOOLEAN,
319            offsetof(struct passwd_instance, ignorenislike), NULL,  "no" },
320         { "hashsize",   PW_TYPE_INTEGER,
321            offsetof(struct passwd_instance, hashsize), NULL,  "100" },
322
323         { NULL, -1, 0, NULL, NULL }             /* end the list */
324 };
325
326 static int passwd_instantiate(CONF_SECTION *conf, void **instance)
327 {
328 #define inst ((struct passwd_instance *)*instance)
329         int nfields=0, keyfield=-1, listable=0;
330         char *s;
331         int len;
332         DICT_ATTR * da;
333         
334         *instance = rad_malloc(sizeof(struct passwd_instance));
335         if (cf_section_parse(conf, inst, module_config) < 0) {
336                 free(inst);
337                 radlog(L_ERR, "rlm_passwd: cann't parse configuration");
338                 return -1;
339         }
340         if(!inst->filename || *inst->filename == '\0' || !inst->format || *inst->format == '\0') {
341                 radlog(L_ERR, "rlm_passwd: cann't find passwd file and/or format in configuration");
342                 return -1;
343         }
344         s = inst->format - 1;
345         do {
346                 if(s == inst->format - 1 || *s == ':'){
347                         if(*(s+1) == '*'){
348                                 keyfield = nfields;
349                                 if(*(s+2) == ','){
350                                         listable = 1;
351                                 }
352                         }
353                         nfields++;
354                 }
355                 s++;
356         }while(*s);
357         if(keyfield < 0) {
358                 radlog(L_ERR, "rlm_passwd: no field market as key in format: %s", inst->format);
359                 return -1;
360         }
361         if (! (inst->ht = build_hash_table (inst->filename, nfields, keyfield, listable, inst->hashsize, inst->ignorenislike, inst->delimiter)) ){ 
362                 radlog(L_ERR, "rlm_passwd: can't build hashtable from passwd file");
363                 return -1;
364         }
365         len = strlen (inst->format)+ nfields * sizeof (char*) + sizeof (struct mypasswd) + 1;
366         if (! (inst->pwdfmt = (struct mypasswd *)rad_malloc(len)) ){
367                 radlog(L_ERR, "rlm_passwd: memory allocation failed");
368                 release_hash_table(inst->ht);
369                 return -1;
370         }
371         if (!string_to_entry(inst->format, nfields, ':', inst->pwdfmt , len)) {
372                 radlog(L_ERR, "rlm_passwd: unable to convert format entry");
373                 release_hash_table(inst->ht);
374                 return -1;
375         }
376         if (*inst->pwdfmt->field[keyfield] == '*') inst->pwdfmt->field[keyfield]++;
377         if (*inst->pwdfmt->field[keyfield] == ',') inst->pwdfmt->field[keyfield]++;
378         if (!*inst->pwdfmt->field[keyfield]) {
379                 radlog(L_ERR, "rlm_passwd: key field is empty");
380                 release_hash_table(inst->ht);
381                 return -1;
382         }
383         if (! (da = dict_attrbyname (inst->pwdfmt->field[keyfield])) ) {
384                 radlog(L_ERR, "rlm_passwd: unable to resolve attribute: %s", inst->pwdfmt->field[keyfield]);
385                 release_hash_table(inst->ht);
386                 return -1;
387         }
388         inst->keyattr = da->attr;
389         inst->keyattrtype = da->type;
390         inst->nfields = nfields;
391         inst->keyfield = keyfield;
392         inst->listable = listable;
393         radlog(L_INFO, "rlm_passwd: nfields: %d keyfield %d(%s) listable: %s", nfields, keyfield, inst->pwdfmt->field[keyfield], listable?"yes":"no");
394         return 0;
395         
396 #undef inst
397 }
398
399 static int passwd_detach (void *instance) {
400 #define inst ((struct passwd_instance *)instance)
401         if(inst->ht) release_hash_table(inst->ht);
402         free(instance);
403         return 0;
404 #undef inst
405 }
406
407 static void addresult (struct passwd_instance * inst, VALUE_PAIR ** vp, struct mypasswd * pw)
408 {
409         int i;
410         VALUE_PAIR *newpair;
411         
412         for (i=0; i<inst->nfields; i++) {
413                 if (inst->pwdfmt->field[i] && *inst->pwdfmt->field[i] && pw->field[i] && i != inst->keyfield) {
414                         if (! (newpair = pairmake (inst->pwdfmt->field[i], pw->field[i], T_OP_EQ))) {
415                                 radlog(L_AUTH, "rlm_passwd: Unable to create %s: %s", inst->pwdfmt->field[i], pw->field[i]);
416                                 return;
417                         }
418                         radlog(L_INFO, "rlm_passwd: Added %s: %s", inst->pwdfmt->field[i], pw->field[i]);
419                         pairadd (vp, newpair);
420                 }
421         }
422 }
423
424 static int passwd_authorize(void *instance, REQUEST *request)
425 {
426 #define inst ((struct passwd_instance *)instance)
427         char * name;
428         char buffer[1024];
429         VALUE_PAIR * key;
430         struct mypasswd * pw;
431         int found = 0;
432         
433         if(!request || !request->packet ||!request->packet->vps)
434          return RLM_MODULE_INVALID;
435         for (key = request->packet->vps;
436          key && (key = pairfind (key, inst->keyattr));
437           key = key->next ){
438                 switch (inst->keyattrtype) {
439                         case PW_TYPE_INTEGER:
440                                 snprintf(buffer, 1024, "%u", key->lvalue);
441                                 name = buffer;
442                                 break;
443                         default:
444                                 name = key->strvalue;
445                 }
446                 if (! (pw = get_pw_nam(name, inst->ht)) ) {
447                         continue;
448                 }
449                 do {
450                         addresult(inst, &request->config_items, pw);
451                 } while ( (pw = get_next(name, inst->ht)) );
452                 found++;
453                 if (!inst->allowmultiple) break;
454         }
455         if(!found) {
456                 return RLM_MODULE_NOTFOUND;
457         }
458         if (inst->authtype && (key = pairmake ("Auth-Type", inst->authtype, T_OP_EQ))) {
459                 radlog(L_INFO, "rlm_passwd: Adding Auth-Type: %s", inst->authtype);
460                 pairadd (&request->config_items, key);
461         }
462         return RLM_MODULE_OK;
463         
464 #undef inst
465 }
466
467 module_t rlm_passwd = {
468         "passwd",
469         RLM_TYPE_THREAD_SAFE,           /* type */
470         NULL,                           /* initialize */
471         passwd_instantiate,             /* instantiation */
472         {
473                 NULL,                   /* authentication */
474                 passwd_authorize,       /* authorization */
475                 NULL,                   /* pre-accounting */
476                 NULL,                   /* accounting */
477                 NULL,                   /* checksimul */
478                 NULL,                   /* pre-proxy */
479                 NULL,                   /* post-proxy */
480                 NULL                    /* post-auth */
481         },
482         passwd_detach,                  /* detach */
483         NULL                            /* destroy */
484 };
485 #endif /* TEST */