srcs/toolbox/pwd.c

00001 /* 
00002  * Copyright (c) 2005-2012 by KoanLogic s.r.l. - All rights reserved.  
00003  */
00004 
00005 #include <sys/stat.h>
00006 #include <sys/types.h>
00007 #include <u/libu_conf.h>
00008 #include <u/libu.h>
00009 #include <toolbox/hmap.h>
00010 #include <toolbox/pwd.h>
00011 
00012 /* in-memory db */
00013 static int u_pwd_db_new (u_pwd_t *pwd);
00014 static int u_pwd_db_term (u_pwd_t *pwd);
00015 static int u_pwd_db_load (u_pwd_t *pwd);
00016 static int u_pwd_db_push (u_pwd_t *pwd, u_pwd_rec_t *rec);
00017 static void __hmap_pwd_rec_free (void *val);  /* hook hmap free */
00018 
00019 /* misc */
00020 static int u_pwd_load (u_pwd_t *pwd);
00021 static int u_pwd_need_reload (u_pwd_t *pwd);
00022 
00023 /* u_pwd_rec_t */
00024 static int u_pwd_rec_new (const char *user, const char *pass, 
00025         const char *opaque, u_pwd_rec_t **prec);
00026 static int u_pwd_retr_mem (u_pwd_t *pwd, const char *user, 
00027         u_pwd_rec_t **prec);
00028 static int u_pwd_retr_res (u_pwd_t *pwd, const char *user, 
00029         u_pwd_rec_t **prec);
00030 
00031 /* res ops */
00032 static int u_pwd_res_open (u_pwd_t *pwd);
00033 static void u_pwd_res_close (u_pwd_t *pwd);
00034 
00035 /* file specialization */
00036 static int __file_open (const char *path, void **pfp);
00037 static void __file_close (void *fp);
00038 static char *__file_load (char *str, int size, void *fp);
00039 static int __file_notify (const char *path, time_t last_update, 
00040         time_t *pnew_update);
00041 
00042 /* a pwd instance context (will be passed along all u_pwd functions) */
00043 struct u_pwd_s
00044 {
00045     void *res_handler;  /* underlying storage resource: FILE*, io_t*, ... */
00046     char res_uri[U_FILENAME_MAX + 1];
00047 
00048     size_t hash_len;            /* hash'd password length */
00049     u_pwd_hash_cb_t cb_hash;    /* hash function for password hiding */
00050 
00051     u_pwd_open_cb_t cb_open;    /* function for opening the db */
00052     u_pwd_load_cb_t cb_load;    /* function for getting db records one by one */
00053     u_pwd_close_cb_t cb_close;  /* function for opening the db */
00054 
00055     u_pwd_notify_cb_t cb_notify;    /* function for notifying changes in the 
00056                                        master copy */
00057 
00058     time_t last_mod;    /* timestamp of master db's last update */
00059     int in_memory;      /* if set access is done via snapshot db */
00060     u_hmap_t *db;       /* in-memory master db snapshot */
00061 };
00062 
00063 /* each pwd line looks like this: "<user>:<password>[:opaque]\n":
00064  * the following holds the line tokenization result */
00065 struct u_pwd_rec_s
00066 {
00067     char *user;     /* credential owner */
00068     char *pass;     /* password (hashed or cleartext) */
00069     char *opaque;   /* optional application specific data (e.g. PSK hint) */
00070 };
00071 
00161 int u_pwd_init (const char *res_uri, u_pwd_open_cb_t cb_open, 
00162         u_pwd_load_cb_t cb_load, u_pwd_close_cb_t cb_close, 
00163         u_pwd_notify_cb_t cb_notify, u_pwd_hash_cb_t cb_hash, 
00164         size_t hash_len, int in_memory, u_pwd_t **ppwd)
00165 {
00166     u_pwd_t *pwd;
00167    
00168     dbg_return_if (res_uri == NULL, ~0);
00169     dbg_return_if (cb_open == NULL, ~0);
00170     dbg_return_if (cb_load == NULL, ~0);
00171     /* cb_close is non-mandatory */
00172     /* cb_notify is non-mandatory */
00173     dbg_return_if (cb_hash && !hash_len, ~0);
00174     dbg_return_if (ppwd == NULL, ~0);
00175 
00176     /* make room for the instance context */
00177     pwd = u_zalloc(sizeof(u_pwd_t));
00178     dbg_err_sif (pwd == NULL);
00179 
00180     /* copy in supplied attributes and methods */
00181     pwd->res_handler = NULL;
00182     dbg_err_if (u_strlcpy(pwd->res_uri, res_uri, sizeof pwd->res_uri));
00183 
00184     pwd->hash_len = hash_len;
00185     pwd->cb_hash = cb_hash;
00186     pwd->cb_open = cb_open;
00187     pwd->cb_load = cb_load;
00188     pwd->cb_close = cb_close;
00189     pwd->cb_notify = cb_notify;
00190     pwd->last_mod = 0;
00191     pwd->in_memory = in_memory;
00192     pwd->db = NULL;
00193 
00194     /* NOTE: don't load to memory if requested (i.e. .in_memory != 0) here:
00195      * it will be done at very first u_pwd_retr() via u_pwd_need_reload() */
00196 
00197     *ppwd = pwd;
00198 
00199     return 0;
00200 err:
00201     u_pwd_term(pwd);
00202     return ~0;
00203 }
00204 
00219 int u_pwd_retr (u_pwd_t *pwd, const char *user, u_pwd_rec_t **prec)
00220 {
00221     dbg_return_if (pwd == NULL, ~0);
00222     dbg_return_if (user == NULL, ~0);
00223     dbg_return_if (prec == NULL, ~0);
00224 
00225     /* if in-memory snapshot is mantained, search there (in case on-storage
00226      * image has changed it will be resync'd automatically) */
00227     if (pwd->in_memory)
00228         return u_pwd_retr_mem(pwd, user, prec);
00229 
00230     return u_pwd_retr_res(pwd, user, prec);
00231 }
00232 
00246 int u_pwd_auth_user (u_pwd_t *pwd, const char *user, const char *password)
00247 {
00248     int rc;
00249     u_pwd_rec_t *rec = NULL;
00250     char *__p = NULL, __pstack[U_PWD_LINE_MAX];
00251 
00252     dbg_return_if (password == NULL, ~0);
00253 
00254     /* retrieve the pwd record */
00255     dbg_err_if (u_pwd_retr(pwd, user, &rec));
00256 
00257     /* hash if requested, otherwise do cleartext cmp */
00258     if (pwd->cb_hash)
00259     {
00260         /* create a buffer that fits the specific hash function */
00261         dbg_err_if ((__p = u_zalloc(pwd->hash_len)) == NULL);
00262         (void) pwd->cb_hash(password, strlen(password), __p);
00263     }
00264     else
00265     {
00266         (void) u_strlcpy(__pstack, password, sizeof __pstack);
00267         __p = __pstack;
00268     }
00269 
00270     rc = strcmp(__p, rec->pass);
00271 
00272     /* free __p if on heap */
00273     if (__p && (__p != __pstack))
00274         u_free(__p);
00275 
00276     /* rec ownership is ours only if hmap doesn't have it */
00277     if (!pwd->in_memory)
00278         u_pwd_rec_free(pwd, rec);
00279 
00280     return rc;
00281 err:
00282     if (__p && (__p != __pstack))
00283         u_free(__p);
00284 
00285     if (!pwd->in_memory && rec)
00286         u_pwd_rec_free(pwd, rec);
00287 
00288     return ~0;
00289 }
00290 
00300 void u_pwd_term (u_pwd_t *pwd)
00301 {
00302     nop_return_if (pwd == NULL, );
00303 
00304     (void) u_pwd_db_term(pwd);
00305 
00306     U_FREE(pwd);
00307 
00308     return;
00309 }
00310 
00327 int u_pwd_init_file (const char *res_uri, u_pwd_hash_cb_t cb_hash, 
00328         size_t hash_len, int in_memory, u_pwd_t **ppwd)
00329 {
00330     return u_pwd_init (res_uri, __file_open, __file_load, __file_close, 
00331         __file_notify, cb_hash, hash_len, in_memory, ppwd);
00332 }
00333 
00346 void u_pwd_rec_free (u_pwd_t *pwd, u_pwd_rec_t *rec)
00347 {
00348     dbg_return_if (pwd == NULL, );
00349     dbg_return_if (rec == NULL, );
00350 
00351     U_FREE(rec->user);
00352     U_FREE(rec->pass);
00353     U_FREE(rec->opaque);
00354 
00355     u_free(rec);
00356 
00357     return;
00358 }
00359 
00370 const char *u_pwd_rec_get_user (u_pwd_rec_t *rec)
00371 {
00372     dbg_return_if (rec == NULL, NULL);
00373     return rec->user;
00374 }
00375 
00386 const char *u_pwd_rec_get_password (u_pwd_rec_t *rec)
00387 {
00388     dbg_return_if (rec == NULL, NULL);
00389     return rec->pass;
00390 }
00391 
00401 const char *u_pwd_rec_get_opaque (u_pwd_rec_t *rec)
00402 {
00403     dbg_return_if (rec == NULL, NULL);
00404     return rec->opaque;
00405 }
00406 
00417 int u_pwd_in_memory (u_pwd_t *pwd)
00418 {
00419     return pwd->in_memory;
00420 }
00421 
00426 static int u_pwd_load (u_pwd_t *pwd)
00427 {
00428     dbg_return_if (pwd == NULL, ~0);
00429     dbg_return_if (!pwd->in_memory, ~0);
00430 
00431     /* wipe away old snapshot */
00432     if (pwd->db)
00433         (void) u_pwd_db_term(pwd);
00434     
00435     /* create new hash map and load master db contents into it */
00436     dbg_err_if (u_pwd_db_new(pwd));
00437     dbg_err_if (u_pwd_db_load(pwd));
00438 
00439     return 0;
00440 err:
00441     return ~0;
00442 }
00443 
00444 static int u_pwd_retr_res (u_pwd_t *pwd, const char *user, 
00445         u_pwd_rec_t **prec)
00446 {
00447     size_t lc, got_it = 0, ntoks;
00448     char ln[U_PWD_LINE_MAX], uu[U_PWD_LINE_MAX];
00449     char **toks = NULL;  /* expected line fmt is: "name:password[:opaque]\n" */
00450     u_pwd_rec_t *rec = NULL;
00451 
00452     dbg_return_if (pwd->res_uri == NULL, ~0);
00453     dbg_return_if (pwd->cb_load == NULL, ~0);
00454     /* cb_open consistency will be checked inside u_pwd_res_open */
00455 
00456     /* open master db */
00457     dbg_err_if (u_pwd_res_open(pwd));
00458 
00459     /* do suitable search string for strstr(3) */
00460     u_snprintf(uu, sizeof uu, "%s:", user);
00461     
00462     /* read line by line */
00463     for (lc = 1; pwd->cb_load(ln, sizeof ln, pwd->res_handler) != NULL; lc++)
00464     {
00465         /* skip comments */
00466         if (ln[0] == '#')
00467             continue;
00468 
00469         /* check if we're on user line, in case break the read loop... 
00470          * this is different from using the in-memory version in case an 
00471          * entry is duplicated: in-memory matches the last entry, here
00472          * we would get the first one */
00473         if (strstr(ln, uu) == ln)
00474         {
00475             got_it = 1;
00476             break;
00477         }
00478     }
00479 
00480     /* check if we've reached here due to simple loop exhaustion */
00481     dbg_err_ifm (!got_it, "user %s not found", user);
00482 
00483     /* remove terminating \n if needed */
00484     if (ln[strlen(ln) - 1] == '\n')
00485         ln[strlen(ln) - 1] = '\0';
00486 
00487     /* tokenize line: expect at least 2 fields: user and password */
00488     dbg_err_ifm (u_strtok(ln, ":", &toks, &ntoks) || ntoks < 2,
00489             "bad syntax at line %zu (%s)", lc, ln);
00490 
00491     /* create new record */
00492     dbg_err_if (u_pwd_rec_new(toks[0], toks[1], ntoks < 3 ? NULL : toks[2], 
00493                 &rec));
00494 
00495     /* dispose u_strtok rubbish */
00496     u_strtok_cleanup(toks, ntoks);
00497 
00498     /* dispose resource handler (if a 'close' method has been set) */
00499     u_pwd_res_close(pwd);
00500 
00501     /* copy out */
00502     *prec = rec;
00503 
00504     return 0;
00505 err:
00506     if (rec)
00507         u_pwd_rec_free(pwd, rec);
00508 
00509     if (toks)
00510         u_strtok_cleanup(toks, ntoks);
00511 
00512     u_pwd_res_close(pwd);
00513 
00514     return ~0;
00515 }
00516 
00517 static int u_pwd_res_open (u_pwd_t *pwd)
00518 {
00519     dbg_return_if (pwd->cb_open == NULL, ~0);
00520 
00521     if (pwd->res_handler != NULL)
00522         u_warn("non-NULL resource handler will be lost");
00523 
00524     pwd->res_handler = NULL;
00525 
00526     return pwd->cb_open(pwd->res_uri, &pwd->res_handler);
00527 }
00528 
00529 static void u_pwd_res_close (u_pwd_t *pwd)
00530 {
00531     nop_return_if (pwd->res_handler == NULL, );
00532     nop_return_if (pwd->cb_close == NULL, );
00533 
00534     pwd->cb_close(pwd->res_handler);
00535     pwd->res_handler = NULL;
00536     
00537     return;
00538 }
00539 
00540 static int u_pwd_rec_new (const char *user, const char *pass, 
00541         const char *opaque, u_pwd_rec_t **prec)
00542 {
00543     u_pwd_rec_t *rec = NULL;
00544 
00545     dbg_return_if (user == NULL, ~0);
00546     dbg_return_if (pass == NULL, ~0);
00547     dbg_return_if (prec == NULL, ~0);
00548 
00549     rec = u_zalloc(sizeof(u_pwd_rec_t));
00550     dbg_err_sif (rec == NULL);
00551 
00552     rec->user = u_strdup(user);
00553     dbg_err_sif (rec->user == NULL);
00554 
00555     rec->pass = u_strdup(pass);
00556     dbg_err_sif (rec->pass == NULL);
00557 
00558     /* opaque field may be NULL */
00559     if (opaque)
00560     {
00561         rec->opaque = u_strdup(opaque);
00562         dbg_err_sif (rec->opaque == NULL);
00563     }
00564 
00565     *prec = rec;
00566 
00567     return 0;
00568 err:
00569     if (rec)
00570     {
00571         U_FREE(rec->user);
00572         U_FREE(rec->pass);
00573         U_FREE(rec->opaque);
00574         u_free(rec);
00575     }
00576     return ~0;
00577 }
00578 
00579 static int u_pwd_retr_mem (u_pwd_t *pwd, const char *user, 
00580         u_pwd_rec_t **prec)
00581 {
00582     u_pwd_rec_t *pr = NULL;
00583 
00584     dbg_return_if (pwd == NULL, ~0);
00585     dbg_return_if (user == NULL, ~0);
00586     dbg_return_if (prec == NULL, ~0);
00587 
00588     /* on error keep on working with the old in-memory db */
00589     dbg_ifb (u_pwd_need_reload(pwd))
00590         u_warn("error reloading master pwd file: using stale cache");
00591 
00592     dbg_err_if (pwd->db == NULL);
00593 
00594     pr = (u_pwd_rec_t *) u_hmap_easy_get(pwd->db, user);
00595     dbg_err_if (pr == NULL);
00596 
00597     *prec = pr;
00598 
00599     return 0;
00600 err:
00601     return ~0;
00602 }
00603 
00604 static int u_pwd_need_reload (u_pwd_t *pwd)
00605 {
00606     time_t update_timestamp;
00607 
00608     /* if needed parameters are not set return immediately (no error) */
00609     nop_return_if (!pwd->in_memory, 0);
00610     nop_return_if (pwd->cb_notify == NULL, 0);
00611 
00612     /* in case no update has been notified return */
00613     if (!pwd->cb_notify(pwd->res_uri, pwd->last_mod, &update_timestamp))
00614         return 0;
00615 
00616     /* update notified: set .last_mod */
00617     pwd->last_mod = update_timestamp;
00618     
00619     /* reload db to memory */
00620     return u_pwd_load(pwd);
00621 }
00622 
00623 static int u_pwd_db_new (u_pwd_t *pwd)
00624 {
00625     u_hmap_opts_t *hopts = NULL;
00626 
00627     dbg_err_if (pwd == NULL);
00628 
00629     dbg_err_if (u_hmap_opts_new(&hopts));
00630     dbg_err_if (u_hmap_opts_set_val_freefunc(hopts, &__hmap_pwd_rec_free));
00631             
00632     return u_hmap_easy_new(hopts, &pwd->db);
00633 err:
00634     if (hopts)
00635         u_hmap_opts_free(hopts);
00636     return ~0;
00637 }
00638 
00639 static int u_pwd_db_term (u_pwd_t *pwd)
00640 {
00641     dbg_return_if (pwd == NULL, ~0);
00642         
00643     nop_return_if (pwd->db == NULL, 0);
00644             
00645     u_hmap_easy_free(pwd->db);
00646     pwd->db = NULL;
00647 
00648     return 0;
00649 }
00650 
00651 static int u_pwd_db_load (u_pwd_t *pwd)
00652 {
00653     size_t lc, ntoks;
00654     char ln[U_PWD_LINE_MAX];
00655     char **toks = NULL;  /* line fmt is: "name:password[:hint]\n" */
00656     u_pwd_rec_t *rec = NULL;
00657     
00658     dbg_return_if (pwd->res_uri == NULL, ~0);
00659     dbg_return_if (pwd->cb_load == NULL, ~0);
00660     /* cb_open and cb_close will be checked inside u_pwd_res_{open,close} */
00661 
00662     /* open master db */
00663     dbg_err_if (u_pwd_res_open(pwd));
00664 
00665     for (lc = 1; pwd->cb_load(ln, sizeof ln, pwd->res_handler) != NULL; lc++)
00666     {
00667         /* cleanup strtok rubbish from the previous loop */
00668         if (toks)
00669         {
00670             u_strtok_cleanup(toks, ntoks);
00671             toks = NULL;
00672         }
00673 
00674         /* skip comment lines */
00675         if (ln[0] == '#')
00676             continue;
00677 
00678         /* remove trailing \n */
00679         if (ln[strlen(ln) - 1] == '\n')
00680             ln[strlen(ln) - 1] = '\0';
00681 
00682         /* tokenize line */
00683         dbg_ifb (u_strtok(ln, ":", &toks, &ntoks) || ntoks < 2)
00684         {
00685             u_info("bad syntax at line %zu (%s)", lc, ln);
00686             continue;
00687         }
00688 
00689         /* create u_pwd_rec_t from tokens */
00690         dbg_ifb (u_pwd_rec_new(toks[0], toks[1], ntoks < 3 ? NULL : toks[2], 
00691                     &rec))
00692         {
00693             u_info("could not create record for entry at line %zu", lc);
00694             continue;
00695         }
00696 
00697         /* push rec to db */
00698         dbg_ifb (u_pwd_db_push(pwd, rec))
00699         {
00700             u_info("could not push record for entry at line %zu", lc);
00701             u_pwd_rec_free(pwd, rec), rec = NULL;
00702         }
00703 
00704         rec = NULL;
00705     }
00706 
00707     if (toks)
00708         u_strtok_cleanup(toks, ntoks);
00709  
00710     if (rec)
00711         u_pwd_rec_free(pwd, rec);
00712 
00713     u_pwd_res_close(pwd);
00714 
00715     return 0;
00716 err:
00717     if (rec)
00718         u_pwd_rec_free(pwd, rec);
00719 
00720     if (toks)
00721         u_strtok_cleanup(toks, ntoks);
00722  
00723     u_pwd_res_close(pwd);
00724 
00725     return ~0;
00726 }
00727 
00728 static int u_pwd_db_push (u_pwd_t *pwd, u_pwd_rec_t *rec)
00729 {
00730     dbg_return_if (pwd->db == NULL, ~0);
00731     dbg_return_if (rec == NULL, ~0);
00732     dbg_return_if (rec->user == NULL, ~0);
00733 
00734     return u_hmap_easy_put(pwd->db, rec->user, (const void *) rec);
00735 }
00736 
00737 /* 
00738  * prefabricated callbacks
00739  */
00740 static int __file_open (const char *path, void **pfp)
00741 {
00742     FILE *fp = NULL;
00743 
00744     dbg_err_sif ((fp = fopen(path, "r")) == NULL);
00745 
00746     *pfp = (void *) fp;
00747 
00748     return 0;
00749 err:
00750     return ~0;
00751 }
00752 
00753 static void __file_close (void *fp)
00754 {
00755     dbg_return_sif (fclose((FILE *) fp), /* nothing */);
00756     return;
00757 }
00758 
00759 static char *__file_load (char *str, int size, void *fp)
00760 {
00761     return fgets(str, size, (FILE *) fp);
00762 }
00763 
00764 /* in case file update has been detected, *pnew_update is also set */
00765 static int __file_notify (const char *path, time_t last_update, 
00766         time_t *pnew_update)
00767 {
00768     struct stat sb;
00769 
00770     dbg_err_if (path == NULL);
00771 
00772     dbg_err_sif (stat(path, &sb));
00773 
00774     if (sb.st_ctime != last_update)
00775     {
00776         *pnew_update = sb.st_ctime;
00777         return 1;
00778     }
00779 
00780     /* fall through (return false) */
00781 err:
00782     return 0;
00783 }
00784 
00785 /* hmap glue */
00786 static void __hmap_pwd_rec_free (void *val)
00787 {
00788     u_pwd_t fake_pwd;
00789 
00790     fake_pwd.in_memory = 1;
00791 
00792     u_pwd_rec_free(&fake_pwd, (u_pwd_rec_t *) val);
00793 
00794     return;
00795 }

←Products
© 2005-2012 - KoanLogic S.r.l. - All rights reserved