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.
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.
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
20 * @brief Enables authentication against unix passwd files.
22 * @copyright 2000,2006 The FreeRADIUS server project
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/modules.h>
28 #include <freeradius-devel/rad_assert.h>
31 struct mypasswd *next;
43 struct mypasswd **table;
52 #define rad_malloc(s) malloc(s)
54 void printpw(struct mypasswd *pw, int nfields){
57 for( i = 0; i < nfields; i++ ) printf("%s:", pw->field[i]);
60 else printf ("Not found\n");
66 static struct mypasswd * mypasswd_malloc(char const* buffer, int nfields, size_t* len)
69 /* reserve memory for (struct mypasswd) + listflag (nfields * sizeof (char*)) +
70 ** fields (nfields * sizeof (char)) + strlen (inst->format) + 1 */
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);
78 static int string_to_entry(char const* string, int nfields, char delimiter,
79 struct mypasswd *passwd, size_t bufferlen)
89 if (string[len-1] == '\n') len--;
91 if (string[len-1] == '\r') len--;
93 if (!len || !passwd ||
94 bufferlen < (len + nfields * sizeof (char*) + nfields * sizeof (char) + sizeof (struct mypasswd) + 1) ) return 0;
96 data_beg=(char *)passwd + sizeof(struct mypasswd);
97 str = data_beg + nfields * sizeof (char) + nfields * sizeof (char*);
98 memcpy (str, string, len);
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) {
105 passwd->field[fn++] = str + i + 1;
106 if (fn == nfields) break;
109 for (; fn < nfields; fn++) passwd->field[fn] = NULL;
110 return len + nfields * sizeof (char) + nfields * sizeof (char*) + sizeof (struct mypasswd) + 1;
114 static void destroy_password (struct mypasswd * pass)
117 while ((p=pass)!=NULL) {
124 static unsigned int hash(char const * username, unsigned int tablesize)
128 h = h * 7907 + *username++;
133 static void release_hash_table(struct hashtable * ht){
137 for (i = 0; i < ht->tablesize; i++)
139 destroy_password(ht->table[i]);
151 static void release_ht(struct hashtable * ht){
153 release_hash_table(ht);
161 static struct hashtable * build_hash_table (char const * file, int nfields,
162 int keyfield, int islist, int tablesize, int ignorenis, char delimiter)
164 struct hashtable* ht;
167 struct mypasswd *hashentry, *hashentry1;
173 ht = (struct hashtable *) rad_malloc(sizeof(struct hashtable));
177 memset(ht, 0, sizeof(struct hashtable));
178 ht->filename = strdup(file);
183 ht->tablesize = tablesize;
184 ht->nfields = nfields;
185 ht->keyfield = keyfield;
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"))) {
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.
202 memset(ht->buffer, 0, 1024);
203 ht->table = (struct mypasswd **) rad_malloc (tablesize * sizeof(struct mypasswd *));
206 * Unable allocate memory for hash table
207 * Still work without it
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);
219 len = string_to_entry(buffer, nfields, ht->delimiter, hashentry, len);
220 if(!hashentry->field[keyfield] || *hashentry->field[keyfield] == '\0') {
226 list = hashentry->field[keyfield];
227 for (nextlist = list; *nextlist && *nextlist!=','; nextlist++);
228 if (*nextlist) *nextlist++ = 0;
231 h = hash(hashentry->field[keyfield], tablesize);
232 hashentry->next = ht->table[h];
233 ht->table[h] = hashentry;
235 for(list=nextlist; nextlist; list = nextlist){
236 for (nextlist = list; *nextlist && *nextlist!=','; nextlist++);
237 if (*nextlist) *nextlist++ = 0;
239 if(!(hashentry1 = mypasswd_malloc("", nfields, &len))){
240 release_hash_table(ht);
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;
258 static struct mypasswd * get_next(char *name, struct hashtable *ht,
259 struct mypasswd **last_found)
261 struct mypasswd * passwd;
262 struct mypasswd * hashentry;
264 char *list, *nextlist;
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;
278 /* printf("try to find in file\n"); */
279 if (!ht->fp) return NULL;
281 passwd = (struct mypasswd *) ht->buffer;
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 != '+') ) ){
287 if(!strcmp(passwd->field[ht->keyfield], name))
291 for (list = passwd->field[ht->keyfield], nextlist = list; nextlist; list = nextlist) {
292 for(nextlist = list; *nextlist && *nextlist!=','; nextlist++);
298 if (!strcmp(list, name)) {
311 static struct mypasswd * get_pw_nam(char * name, struct hashtable* ht,
312 struct mypasswd **last_found)
315 struct mypasswd * hashentry;
317 if (!ht || !name || *name == '\0') return 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;
335 if (!(ht->fp=fopen(ht->filename, "r"))) return NULL;
336 return get_next(name, ht, last_found);
341 #define MALLOC_CHECK_ 1
344 struct hashtable *ht;
346 struct mypasswd* pw, *last_found;
349 ht = build_hash_table("/etc/group", 4, 3, 1, 100, 0, ":");
351 printf("Hash table not built\n");
354 for (i = 0; i < ht->tablesize; i++) {
357 for (pw = ht->table[i]; pw; pw = pw->next) {
363 while(fgets(buffer, 1024, stdin)){
364 buffer[strlen(buffer)-1] = 0;
365 pw = get_pw_nam(buffer, ht, &last_found);
367 while ((pw = get_next(buffer, ht, &last_found))) printpw(pw,4);
373 typedef struct rlm_passwd_t {
374 struct hashtable *ht;
375 struct mypasswd *pwdfmt;
376 char const *filename;
378 char const *delimiter;
385 DICT_ATTR const *keyattr;
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), ":" },
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" },
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" },
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" },
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
408 static int mod_instantiate(CONF_SECTION *conf, void *instance)
410 int nfields=0, keyfield=-1, listable=0;
412 char *lf=NULL; /* destination list flags temporary */
415 DICT_ATTR const * da;
416 rlm_passwd_t *inst = instance;
418 rad_assert(inst->filename && *inst->filename);
419 rad_assert(inst->format && *inst->format);
421 if (inst->hash_size == 0) {
422 cf_log_err_cs(conf, "Invalid value '0' for hash_size");
426 lf = talloc_typed_strdup(inst, inst->format);
428 ERROR("rlm_passwd: memory allocation failed for lf");
431 memset(lf, 0, strlen(inst->format));
432 s = inst->format - 1;
434 if(s == inst->format - 1 || *s == ':'){
456 cf_log_err_cs(conf, "no field marked as key in format: %s",
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");
464 if (! (inst->pwdfmt = mypasswd_malloc(inst->format, nfields, &len)) ){
465 ERROR("rlm_passwd: memory allocation failed");
466 release_ht(inst->ht);
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);
477 memcpy(inst->pwdfmt->listflag, lf, nfields);
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]++;
486 if (!*inst->pwdfmt->field[keyfield]) {
487 cf_log_err_cs(conf, "key field is empty");
488 release_ht(inst->ht);
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);
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");
508 static int mod_detach (void *instance) {
509 #define inst ((rlm_passwd_t *)instance)
511 release_ht(inst->ht);
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)
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);
530 RDEBUG("Added %s: '%s' to %s ", inst->pwdfmt->field[i], pw->field[i], listname);
533 RDEBUG("NOOP %s: '%s' to %s ", inst->pwdfmt->field[i], pw->field[i], listname);
538 static rlm_rcode_t CC_HINT(nonnull) mod_passwd_map(void *instance, REQUEST *request)
540 #define inst ((rlm_passwd_t *)instance)
543 struct mypasswd * pw, *last_found;
547 key = fr_pair_find_by_da(request->packet->vps, inst->keyattr, TAG_ANY);
549 return RLM_MODULE_NOTFOUND;
552 for (i = fr_cursor_init(&cursor, &key);
554 i = fr_cursor_next_by_num(&cursor, inst->keyattr->attr, inst->keyattr->vendor, TAG_ANY)) {
556 * Ensure we have the string form of the attribute
558 vp_prints_value(buffer, sizeof(buffer), i, 0);
559 if (!(pw = get_pw_nam(buffer, inst->ht, &last_found)) ) {
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)));
570 if (!inst->allow_multiple) {
575 if (!found) return RLM_MODULE_NOTFOUND;
577 return RLM_MODULE_OK;
582 extern module_t rlm_passwd;
583 module_t rlm_passwd = {
584 .magic = RLM_MODULE_INIT,
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,
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,
598 [MOD_RECV_COA] = mod_passwd_map,
599 [MOD_SEND_COA] = mod_passwd_map