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, version 2 if the
4 * License as published by the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 * @brief Enables authentication against unix passwd files.
21 * @copyright 2000,2006 The FreeRADIUS server project
25 #include <freeradius-devel/radiusd.h>
26 #include <freeradius-devel/modules.h>
27 #include <freeradius-devel/rad_assert.h>
30 struct mypasswd *next;
42 struct mypasswd **table;
51 #define rad_malloc(s) malloc(s)
53 void printpw(struct mypasswd *pw, int nfields){
56 for( i = 0; i < nfields; i++ ) printf("%s:", pw->field[i]);
59 else printf ("Not found\n");
65 static struct mypasswd * mypasswd_malloc(char const* buffer, int nfields, size_t* len)
68 /* reserve memory for (struct mypasswd) + listflag (nfields * sizeof (char*)) +
69 ** fields (nfields * sizeof (char)) + strlen (inst->format) + 1 */
71 *len=sizeof (struct mypasswd) + nfields * sizeof (char*) + nfields * sizeof (char ) + strlen(buffer) + 1;
72 t = (struct mypasswd *) rad_malloc(*len);
73 if (t) memset(t, 0, *len);
77 static int string_to_entry(char const* string, int nfields, char delimiter,
78 struct mypasswd *passwd, size_t bufferlen)
88 if (string[len-1] == '\n') len--;
90 if (string[len-1] == '\r') len--;
92 if (!len || !passwd ||
93 bufferlen < (len + nfields * sizeof (char*) + nfields * sizeof (char) + sizeof (struct mypasswd) + 1) ) return 0;
95 data_beg=(char *)passwd + sizeof(struct mypasswd);
96 str = data_beg + nfields * sizeof (char) + nfields * sizeof (char*);
97 memcpy (str, string, len);
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) {
104 passwd->field[fn++] = str + i + 1;
105 if (fn == nfields) break;
108 for (; fn < nfields; fn++) passwd->field[fn] = NULL;
109 return len + nfields * sizeof (char) + nfields * sizeof (char*) + sizeof (struct mypasswd) + 1;
113 static void destroy_password (struct mypasswd * pass)
116 while ((p=pass)!=NULL) {
123 static unsigned int hash(char const * username, unsigned int tablesize)
127 h = h * 7907 + *username++;
132 static void release_hash_table(struct hashtable * ht){
136 for (i = 0; i < ht->tablesize; i++)
138 destroy_password(ht->table[i]);
150 static void release_ht(struct hashtable * ht){
152 release_hash_table(ht);
160 static struct hashtable * build_hash_table (char const * file, int nfields,
161 int keyfield, int islist, int tablesize, int ignorenis, char delimiter)
163 struct hashtable* ht;
166 struct mypasswd *hashentry, *hashentry1;
172 ht = (struct hashtable *) rad_malloc(sizeof(struct hashtable));
176 memset(ht, 0, sizeof(struct hashtable));
177 ht->filename = strdup(file);
182 ht->tablesize = tablesize;
183 ht->nfields = nfields;
184 ht->keyfield = keyfield;
186 ht->ignorenis = ignorenis;
187 if (delimiter) ht->delimiter = delimiter;
188 else ht->delimiter = ':';
189 if(!tablesize) return ht;
190 if(!(ht->fp = fopen(file,"r"))) {
197 * @todo: This code is SHIT. It's badly formatted. It's
198 * hard to understand. It re-implements tons of things
199 * which are already in the server core.
201 memset(ht->buffer, 0, 1024);
202 ht->table = (struct mypasswd **) rad_malloc (tablesize * sizeof(struct mypasswd *));
205 * Unable allocate memory for hash table
206 * Still work without it
211 memset(ht->table, 0, tablesize * sizeof(struct mypasswd *));
212 while (fgets(buffer, 1024, ht->fp)) {
213 if(*buffer && *buffer!='\n' && (!ignorenis || (*buffer != '+' && *buffer != '-')) ){
214 if(!(hashentry = mypasswd_malloc(buffer, nfields, &len))){
215 release_hash_table(ht);
218 len = string_to_entry(buffer, nfields, ht->delimiter, hashentry, len);
219 if(!hashentry->field[keyfield] || *hashentry->field[keyfield] == '\0') {
225 list = hashentry->field[keyfield];
226 for (nextlist = list; *nextlist && *nextlist!=','; nextlist++);
227 if (*nextlist) *nextlist++ = 0;
230 h = hash(hashentry->field[keyfield], tablesize);
231 hashentry->next = ht->table[h];
232 ht->table[h] = hashentry;
234 for(list=nextlist; nextlist; list = nextlist){
235 for (nextlist = list; *nextlist && *nextlist!=','; nextlist++);
236 if (*nextlist) *nextlist++ = 0;
238 if(!(hashentry1 = mypasswd_malloc("", nfields, &len))){
239 release_hash_table(ht);
242 for (i=0; i<nfields; i++) hashentry1->field[i] = hashentry->field[i];
243 hashentry1->field[keyfield] = list;
244 h = hash(list, tablesize);
245 hashentry1->next = ht->table[h];
246 ht->table[h] = hashentry1;
257 static struct mypasswd * get_next(char *name, struct hashtable *ht,
258 struct mypasswd **last_found)
260 struct mypasswd * passwd;
261 struct mypasswd * hashentry;
263 char *list, *nextlist;
265 if (ht->tablesize > 0) {
266 /* get saved address of next item to check from buffer */
267 hashentry = *last_found;
268 for (; hashentry; hashentry = hashentry->next) {
269 if (!strcmp(hashentry->field[ht->keyfield], name)) {
270 /* save new address */
271 *last_found = hashentry->next;
277 /* printf("try to find in file\n"); */
278 if (!ht->fp) return NULL;
280 passwd = (struct mypasswd *) ht->buffer;
282 while (fgets(buffer, 1024,ht->fp)) {
283 if(*buffer && *buffer!='\n' && string_to_entry(buffer, ht->nfields, ht->delimiter, passwd, sizeof(ht->buffer)-1) &&
284 (!ht->ignorenis || (*buffer !='-' && *buffer != '+') ) ){
286 if(!strcmp(passwd->field[ht->keyfield], name))
290 for (list = passwd->field[ht->keyfield], nextlist = list; nextlist; list = nextlist) {
291 for(nextlist = list; *nextlist && *nextlist!=','; nextlist++);
297 if (!strcmp(list, name)) {
310 static struct mypasswd * get_pw_nam(char * name, struct hashtable* ht,
311 struct mypasswd **last_found)
314 struct mypasswd * hashentry;
316 if (!ht || !name || *name == '\0') return NULL;
318 if (ht->tablesize > 0) {
319 h = hash (name, ht->tablesize);
320 for (hashentry = ht->table[h]; hashentry; hashentry = hashentry->next) {
321 if (!strcmp(hashentry->field[ht->keyfield], name)){
322 /* save address of next item to check into buffer */
323 *last_found=hashentry->next;
334 if (!(ht->fp=fopen(ht->filename, "r"))) return NULL;
335 return get_next(name, ht, last_found);
340 #define MALLOC_CHECK_ 1
343 struct hashtable *ht;
345 struct mypasswd* pw, *last_found;
348 ht = build_hash_table("/etc/group", 4, 3, 1, 100, 0, ":");
350 printf("Hash table not built\n");
353 for (i = 0; i < ht->tablesize; i++) {
356 for (pw = ht->table[i]; pw; pw = pw->next) {
362 while(fgets(buffer, 1024, stdin)){
363 buffer[strlen(buffer)-1] = 0;
364 pw = get_pw_nam(buffer, ht, &last_found);
366 while ((pw = get_next(buffer, ht, &last_found))) printpw(pw,4);
372 struct passwd_instance {
373 struct hashtable *ht;
374 struct mypasswd *pwdfmt;
375 char const *filename;
377 char const *delimiter;
384 DICT_ATTR const *keyattr;
388 static const CONF_PARSER module_config[] = {
389 { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_REQUIRED, struct passwd_instance, filename), NULL },
390 { "format", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, struct passwd_instance, format), NULL },
391 { "delimiter", FR_CONF_OFFSET(PW_TYPE_STRING, struct passwd_instance, delimiter), ":" },
393 { "ignorenislike", FR_CONF_OFFSET(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, struct passwd_instance, ignore_nislike), NULL },
394 { "ignore_nislike", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, struct passwd_instance, ignore_nislike), "yes" },
396 { "ignoreempty", FR_CONF_OFFSET(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, struct passwd_instance, ignore_empty), NULL },
397 { "ignore_empty", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, struct passwd_instance, ignore_empty), "yes" },
399 { "allowmultiplekeys", FR_CONF_OFFSET(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, struct passwd_instance, allow_multiple), NULL },
400 { "allow_multiple_keys", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, struct passwd_instance, allow_multiple), "no" },
402 { "hashsize", FR_CONF_OFFSET(PW_TYPE_INTEGER | PW_TYPE_DEPRECATED, struct passwd_instance, hash_size), NULL },
403 { "hash_size", FR_CONF_OFFSET(PW_TYPE_INTEGER, struct passwd_instance, hash_size), "100" },
405 { NULL, -1, 0, NULL, NULL }
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 struct passwd_instance *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 ((struct passwd_instance *)instance)
511 release_ht(inst->ht);
519 static void addresult (TALLOC_CTX *ctx, struct passwd_instance *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 = pairmake(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 ((struct passwd_instance *)instance)
543 struct mypasswd * pw, *last_found;
546 key = pair_find_by_da(request->packet->vps, inst->keyattr, TAG_ANY);
548 return RLM_MODULE_NOTFOUND;
551 for (i = fr_cursor_init(&cursor, &key);
553 i = fr_cursor_next_by_num(&cursor, inst->keyattr->attr, inst->keyattr->vendor, TAG_ANY)) {
555 * Ensure we have the string form of the attribute
557 vp_prints_value(buffer, sizeof(buffer), i, 0);
558 if (!(pw = get_pw_nam(buffer, inst->ht, &last_found)) ) {
562 addresult(request, inst, request, &request->config_items, pw, 0, "config_items");
563 addresult(request->reply, inst, request, &request->reply->vps, pw, 1, "reply_items");
564 addresult(request->packet, inst, request, &request->packet->vps, pw, 2, "request_items");
565 } while ((pw = get_next(buffer, inst->ht, &last_found)));
567 if (!inst->allow_multiple) {
572 return RLM_MODULE_OK;
577 extern module_t rlm_passwd;
578 module_t rlm_passwd = {
581 RLM_TYPE_HUP_SAFE, /* type */
582 sizeof(struct passwd_instance),
584 mod_instantiate, /* instantiation */
585 mod_detach, /* detach */
587 NULL, /* authentication */
588 mod_passwd_map, /* authorization */
589 NULL, /* pre-accounting */
590 mod_passwd_map, /* accounting */
591 NULL, /* checksimul */
592 NULL, /* pre-proxy */
593 NULL, /* post-proxy */
594 mod_passwd_map /* post-auth */