Main Page | Modules | Data Structures | File List | Data Fields

pwd.c

00001 /* 
00002  * Copyright (c) 2005-2008 by KoanLogic s.r.l. - All rights reserved.  
00003  */
00004 
00005 static const char rcsid[] =
00006     "$Id: pwd.c,v 1.10 2008/04/02 10:30:43 tho Exp $";
00007 
00008 #include <sys/stat.h>
00009 #include <sys/types.h>
00010 #include <u/libu_conf.h>
00011 #include <u/libu.h>
00012 #include <toolbox/hmap.h>
00013 #include <toolbox/pwd.h>
00014 
00015 /* in-memory db */
00016 static int u_pwd_db_new (u_pwd_t *pwd);
00017 static int u_pwd_db_term (u_pwd_t *pwd);
00018 static int u_pwd_db_load (u_pwd_t *pwd);
00019 static int u_pwd_db_push (u_pwd_t *pwd, u_pwd_rec_t *rec);
00020 static void __hmap_pwd_rec_free (u_hmap_o_t *obj);  /* hook hmap free */
00021 
00022 /* misc */
00023 static int u_pwd_load (u_pwd_t *pwd);
00024 static int u_pwd_need_reload (u_pwd_t *pwd);
00025 
00026 /* u_pwd_rec_t */
00027 static int u_pwd_rec_new (const char *user, const char *pass, 
00028         const char *opaque, u_pwd_rec_t **prec);
00029 static int u_pwd_retr_mem (u_pwd_t *pwd, const char *user, 
00030         u_pwd_rec_t **prec);
00031 static int u_pwd_retr_res (u_pwd_t *pwd, const char *user, 
00032         u_pwd_rec_t **prec);
00033 
00034 /* res ops */
00035 static int u_pwd_res_open (u_pwd_t *pwd);
00036 static void u_pwd_res_close (u_pwd_t *pwd);
00037 
00038 /* file specialization */
00039 static int __file_open (const char *path, void **pfp);
00040 static void __file_close (void *fp);
00041 static char *__file_load (char *str, int size, void *fp);
00042 static int __file_notify (const char *path, time_t last_update, 
00043         time_t *pnew_update);
00044 
00045 /* a pwd instance context (will be passed along all u_pwd functions) */
00046 struct u_pwd_s
00047 {
00048     void *res_handler;  /* underlying storage resource: FILE*, io_t*, ... */
00049     char res_uri[U_FILENAME_MAX + 1];
00050 
00051     size_t hash_len;            /* hash'd password length */
00052     u_pwd_hash_cb_t cb_hash;    /* hash function for password hiding */
00053 
00054     u_pwd_open_cb_t cb_open;    /* function for opening the db */
00055     u_pwd_load_cb_t cb_load;    /* function for getting db records one by one */
00056     u_pwd_close_cb_t cb_close;  /* function for opening the db */
00057 
00058     u_pwd_notify_cb_t cb_notify;    /* function for notifying changes in the 
00059                                        master copy */
00060 
00061     time_t last_mod;    /* timestamp of master db's last update */
00062     int in_memory;      /* if set access is done via snapshot db */
00063     u_hmap_t *db;       /* in-memory master db snapshot */
00064 };
00065 
00066 /* each pwd line looks like this: "<user>:<password>[:opaque]\n":
00067  * the following holds the line tokenization result */
00068 struct u_pwd_rec_s
00069 {
00070     char *user;     /* credential owner */
00071     char *pass;     /* password (hashed or cleartext) */
00072     char *opaque;   /* optional application specific data (e.g. PSK hint) */
00073 };
00074 
00098 int u_pwd_init (const char *res_uri, u_pwd_open_cb_t cb_open, 
00099         u_pwd_load_cb_t cb_load, u_pwd_close_cb_t cb_close, 
00100         u_pwd_notify_cb_t cb_notify, u_pwd_hash_cb_t cb_hash, 
00101         size_t hash_len, int in_memory, u_pwd_t **ppwd)
00102 {
00103     u_pwd_t *pwd;
00104    
00105     dbg_return_if (res_uri == NULL, ~0);
00106     dbg_return_if (cb_open == NULL, ~0);
00107     dbg_return_if (cb_load == NULL, ~0);
00108     /* cb_close is non-mandatory */
00109     /* cb_notify is non-mandatory */
00110     dbg_return_if (cb_hash && !hash_len, ~0);
00111     dbg_return_if (ppwd == NULL, ~0);
00112 
00113     /* make room for the instance context */
00114     pwd = u_zalloc(sizeof(u_pwd_t));
00115     dbg_err_sif (pwd == NULL);
00116 
00117     /* copy in supplied attributes and methods */
00118     pwd->res_handler = NULL;
00119     strlcpy(pwd->res_uri, res_uri, sizeof pwd->res_uri);
00120 
00121     pwd->hash_len = hash_len;
00122     pwd->cb_hash = cb_hash;
00123     pwd->cb_open = cb_open;
00124     pwd->cb_load = cb_load;
00125     pwd->cb_close = cb_close;
00126     pwd->cb_notify = cb_notify;
00127     pwd->last_mod = 0;
00128     pwd->in_memory = in_memory;
00129     pwd->db = NULL;
00130 
00131     /* NOTE: don't load to memory if requested (i.e. .in_memory != 0) here:
00132      * it will be done at very first u_pwd_retr() via u_pwd_need_reload() */
00133 
00134     *ppwd = pwd;
00135 
00136     return 0;
00137 err:
00138     u_pwd_term(pwd);
00139     return ~0;
00140 }
00141 
00152 int u_pwd_retr (u_pwd_t *pwd, const char *user, u_pwd_rec_t **prec)
00153 {
00154     dbg_return_if (pwd == NULL, ~0);
00155     dbg_return_if (user == NULL, ~0);
00156     dbg_return_if (prec == NULL, ~0);
00157 
00158     /* if in-memory snapshot is mantained, search there (in case on-storage
00159      * image has changed it will be resync'd automatically) */
00160     if (pwd->in_memory)
00161         return u_pwd_retr_mem(pwd, user, prec);
00162 
00163     return u_pwd_retr_res(pwd, user, prec);
00164 }
00165 
00175 int u_pwd_auth_user (u_pwd_t *pwd, const char *user, const char *password)
00176 {
00177     int rc;
00178     u_pwd_rec_t *rec = NULL;
00179     char *__p = NULL, __pstack[U_PWD_LINE_MAX];
00180 
00181     /* retrieve the pwd record */
00182     dbg_err_if (u_pwd_retr(pwd, user, &rec));
00183 
00184     /* hash if requested, otherwise do cleartext cmp */
00185     if (pwd->cb_hash)
00186     {
00187         /* create a buffer that fits the specific hash function */
00188         dbg_err_if ((__p = u_zalloc(pwd->hash_len)) == NULL);
00189         (void) pwd->cb_hash(password, strlen(password), __p);
00190     }
00191     else
00192     {
00193         (void) strlcpy(__pstack, password, sizeof __pstack);
00194         __p = __pstack;
00195     }
00196 
00197     rc = strcmp(__p, rec->pass);
00198 
00199     /* free __p if on heap */
00200     if (__p && (__p != __pstack))
00201         u_free(__p);
00202 
00203     /* rec ownership is ours only if hmap doesn't have it */
00204     if (!pwd->in_memory)
00205         u_pwd_rec_free(pwd, rec);
00206 
00207     return rc;
00208 err:
00209     if (__p && (__p != __pstack))
00210         u_free(__p);
00211 
00212     if (!pwd->in_memory && rec)
00213         u_pwd_rec_free(pwd, rec);
00214 
00215     return ~0;
00216 }
00217 
00225 void u_pwd_term (u_pwd_t *pwd)
00226 {
00227     nop_return_if (pwd == NULL, );
00228 
00229     (void) u_pwd_db_term(pwd);
00230 
00231     U_FREE(pwd);
00232 
00233     return;
00234 }
00235 
00249 int u_pwd_init_file (const char *res_uri, u_pwd_hash_cb_t cb_hash, 
00250         size_t hash_len, int in_memory, u_pwd_t **ppwd)
00251 {
00252     return u_pwd_init (res_uri, __file_open, __file_load, __file_close, 
00253         __file_notify, cb_hash, hash_len, in_memory, ppwd);
00254 }
00255 
00265 void u_pwd_rec_free (u_pwd_t *pwd, u_pwd_rec_t *rec)
00266 {
00267     dbg_return_if (pwd == NULL, );
00268     dbg_return_if (rec == NULL, );
00269 
00270     /* only records coming from non hash-map'd pwd's shall be free'd */
00271     nop_return_if (pwd->in_memory, );
00272 
00273     U_FREE(rec->user);
00274     U_FREE(rec->pass);
00275     U_FREE(rec->opaque);
00276 
00277     u_free(rec);
00278 
00279     return;
00280 }
00281 
00289 const char *u_pwd_rec_get_user (u_pwd_rec_t *rec)
00290 {
00291     dbg_return_if (rec == NULL, NULL);
00292     return rec->user;
00293 }
00294 
00302 const char *u_pwd_rec_get_password (u_pwd_rec_t *rec)
00303 {
00304     dbg_return_if (rec == NULL, NULL);
00305     return rec->pass;
00306 }
00307 
00315 const char *u_pwd_rec_get_opaque (u_pwd_rec_t *rec)
00316 {
00317     dbg_return_if (rec == NULL, NULL);
00318     return rec->opaque;
00319 }
00320 
00328 int u_pwd_in_memory (u_pwd_t *pwd)
00329 {
00330     return pwd->in_memory;
00331 }
00332 
00337 static int u_pwd_load (u_pwd_t *pwd)
00338 {
00339     dbg_return_if (pwd == NULL, ~0);
00340     dbg_return_if (!pwd->in_memory, ~0);
00341 
00342     /* wipe away old snapshot */
00343     if (pwd->db)
00344         (void) u_pwd_db_term(pwd);
00345     
00346     /* create new hash map and load master db contents into it */
00347     dbg_err_if (u_pwd_db_new(pwd));
00348     dbg_err_if (u_pwd_db_load(pwd));
00349 
00350     return 0;
00351 err:
00352     return ~0;
00353 }
00354 
00355 static int u_pwd_retr_res (u_pwd_t *pwd, const char *user, 
00356         u_pwd_rec_t **prec)
00357 {
00358     size_t lc, got_it = 0;
00359     char ln[U_PWD_LINE_MAX], __user[U_PWD_LINE_MAX];
00360     char *toks[3 + 1];  /* line fmt is: "name:password[:opaque]\n" */
00361     u_pwd_rec_t *rec = NULL;
00362 
00363     dbg_return_if (pwd->res_uri == NULL, ~0);
00364     dbg_return_if (pwd->cb_load == NULL, ~0);
00365     /* cb_open consistency will be checked inside u_pwd_res_open */
00366 
00367     /* open master db */
00368     dbg_err_if (u_pwd_res_open(pwd));
00369 
00370     /* do suitable search string for strstr(3) */
00371     u_snprintf(__user, sizeof __user, "%s:", user);
00372     
00373     /* read line by line */
00374     for (lc = 1; pwd->cb_load(ln, sizeof ln, pwd->res_handler) != NULL; lc++)
00375     {
00376         /* skip comments */
00377         if (ln[0] == '#')
00378             continue;
00379 
00380         /* check if we're on user line, in case break the read loop... 
00381          * this is different from using the in-memory version in case an 
00382          * entry is duplicated: in-memory matches the last entry, here
00383          * we would get the first one */
00384         if (strstr(ln, __user) == ln)
00385         {
00386             got_it = 1;
00387             break;
00388         }
00389     }
00390 
00391     /* check if we've reached here due to simple loop exhaustion */
00392     dbg_err_ifm (!got_it, "user %s not found", user);
00393 
00394     /* remove terminating \n if needed */
00395     if (ln[strlen(ln) - 1] == '\n')
00396         ln[strlen(ln) - 1] = '\0';
00397 
00398     /* tokenize line */
00399     dbg_err_ifm (u_tokenize(ln, ":", toks, 3), 
00400             "bad syntax at line %zu (%s)", lc, ln);
00401 
00402     /* create new record to be given back */
00403     dbg_err_if (u_pwd_rec_new(toks[0], toks[1], toks[2], &rec));
00404 
00405     /* dispose resource handler (if a 'close' method has been set) */
00406     u_pwd_res_close(pwd);
00407 
00408     /* copy out */
00409     *prec = rec;
00410 
00411     return 0;
00412 err:
00413     if (rec)
00414         u_pwd_rec_free(pwd, rec);
00415 
00416     u_pwd_res_close(pwd);
00417 
00418     return ~0;
00419 }
00420 
00421 static int u_pwd_res_open (u_pwd_t *pwd)
00422 {
00423     dbg_return_if (pwd->cb_open == NULL, ~0);
00424 
00425     if (pwd->res_handler != NULL)
00426         warn("non-NULL resource handler will be lost");
00427 
00428     pwd->res_handler = NULL;
00429 
00430     return pwd->cb_open(pwd->res_uri, &pwd->res_handler);
00431 }
00432 
00433 static void u_pwd_res_close (u_pwd_t *pwd)
00434 {
00435     nop_return_if (pwd->res_handler == NULL, );
00436     nop_return_if (pwd->cb_close == NULL, );
00437 
00438     pwd->cb_close(pwd->res_handler);
00439     pwd->res_handler = NULL;
00440     
00441     return;
00442 }
00443 
00444 static int u_pwd_rec_new (const char *user, const char *pass, 
00445         const char *opaque, u_pwd_rec_t **prec)
00446 {
00447     u_pwd_rec_t *rec = NULL;
00448 
00449     dbg_return_if (user == NULL, ~0);
00450     dbg_return_if (pass == NULL, ~0);
00451     dbg_return_if (prec == NULL, ~0);
00452 
00453     rec = u_zalloc(sizeof(u_pwd_rec_t));
00454     dbg_err_sif (rec == NULL);
00455 
00456     rec->user = u_strdup(user);
00457     dbg_err_sif (rec->user == NULL);
00458 
00459     rec->pass = u_strdup(pass);
00460     dbg_err_sif (rec->pass == NULL);
00461 
00462     /* opaque field may be NULL */
00463     if (opaque)
00464     {
00465         rec->opaque = u_strdup(opaque);
00466         dbg_err_sif (rec->opaque == NULL);
00467     }
00468 
00469     *prec = rec;
00470 
00471     return 0;
00472 err:
00473     if (rec)
00474     {
00475         U_FREE(rec->user);
00476         U_FREE(rec->pass);
00477         U_FREE(rec->opaque);
00478         u_free(rec);
00479     }
00480     return ~0;
00481 }
00482 
00483 static int u_pwd_retr_mem (u_pwd_t *pwd, const char *user, 
00484         u_pwd_rec_t **prec)
00485 {
00486     u_hmap_o_t *hobj = NULL;
00487 
00488     dbg_return_if (pwd == NULL, ~0);
00489     dbg_return_if (user == NULL, ~0);
00490     dbg_return_if (prec == NULL, ~0);
00491 
00492     /* on error keep on working with the old in-memory db */
00493     dbg_ifb (u_pwd_need_reload(pwd))
00494         warn("error reloading master pwd file: using stale cache");
00495 
00496     dbg_err_if (pwd->db == NULL);
00497     dbg_err_if (u_hmap_get(pwd->db, user, &hobj));
00498     *prec = (u_pwd_rec_t *) hobj->val;
00499 
00500     return 0;
00501 err:
00502     return ~0;
00503 }
00504 
00505 static int u_pwd_need_reload (u_pwd_t *pwd)
00506 {
00507     time_t update_timestamp;
00508 
00509     /* if needed parameters are not set return immediately (no error) */
00510     nop_return_if (!pwd->in_memory, 0);
00511     nop_return_if (pwd->cb_notify == NULL, 0);
00512 
00513     /* in case no update has been notified return */
00514     if (!pwd->cb_notify(pwd->res_uri, pwd->last_mod, &update_timestamp))
00515         return 0;
00516 
00517     /* update notified: set .last_mod */
00518     pwd->last_mod = update_timestamp;
00519     
00520     /* reload db to memory */
00521     return u_pwd_load(pwd);
00522 }
00523 
00524 static int u_pwd_db_new (u_pwd_t *pwd)
00525 {
00526     u_hmap_opts_t hopts;
00527 
00528     dbg_return_if (pwd == NULL, ~0);
00529 
00530     u_hmap_opts_init(&hopts);
00531     hopts.options |= U_HMAP_OPTS_OWNSDATA;
00532     hopts.f_free = __hmap_pwd_rec_free;
00533             
00534     return u_hmap_new(&hopts, &pwd->db);
00535 }
00536 
00537 static int u_pwd_db_term (u_pwd_t *pwd)
00538 {
00539     dbg_return_if (pwd == NULL, ~0);
00540         
00541     nop_return_if (pwd->db == NULL, 0);
00542             
00543     u_hmap_free(pwd->db);
00544     pwd->db = NULL;
00545 
00546     return 0;
00547 }
00548 
00549 static int u_pwd_db_load (u_pwd_t *pwd)
00550 {
00551     size_t lc;
00552     char ln[U_PWD_LINE_MAX];
00553     char *toks[3 + 1];  /* line fmt is: "name:password[:hint]\n" */
00554     u_pwd_rec_t *rec = NULL;
00555     
00556     dbg_return_if (pwd->res_uri == NULL, ~0);
00557     dbg_return_if (pwd->cb_load == NULL, ~0);
00558     /* cb_open and cb_close will be checked inside u_pwd_res_{open,close} */
00559 
00560     /* open master db */
00561     dbg_err_if (u_pwd_res_open(pwd));
00562 
00563     for (lc = 1; pwd->cb_load(ln, sizeof ln, pwd->res_handler) != NULL; lc++)
00564     {
00565         /* skip comment lines */
00566         if (ln[0] == '#')
00567             continue;
00568 
00569         /* remove trailing \n */
00570         if (ln[strlen(ln) - 1] == '\n')
00571             ln[strlen(ln) - 1] = '\0';
00572 
00573         /* tokenize line */
00574         dbg_ifb (u_tokenize(ln, ":", toks, 3))
00575         {
00576             info("bad syntax at line %zu (%s)", lc, ln);
00577             continue;
00578         }
00579 
00580         /* create u_pwd_rec_t from tokens */
00581         dbg_ifb (u_pwd_rec_new(toks[0], toks[1], toks[2], &rec))
00582         {
00583             info("could not create record for entry at line %zu", lc);
00584             continue;
00585         }
00586 
00587         /* push rec to db */
00588         dbg_ifb (u_pwd_db_push(pwd, rec))
00589         {
00590             info("could not push record for entry at line %zu", lc);
00591             u_pwd_rec_free(pwd, rec), rec = NULL;
00592         }
00593 
00594         rec = NULL;
00595     }
00596 
00597     if (rec)
00598         u_pwd_rec_free(pwd, rec);
00599 
00600     u_pwd_res_close(pwd);
00601 
00602     return 0;
00603 err:
00604     if (rec)
00605         u_pwd_rec_free(pwd, rec);
00606 
00607     u_pwd_res_close(pwd);
00608 
00609     return ~0;
00610 }
00611 
00612 static int u_pwd_db_push (u_pwd_t *pwd, u_pwd_rec_t *rec)
00613 {
00614     char *hkey = NULL;
00615     u_hmap_o_t *hobj = NULL;
00616 
00617     dbg_return_if (pwd->db == NULL, ~0);
00618     dbg_return_if (rec == NULL, ~0);
00619     dbg_return_if (rec->user == NULL, ~0);
00620 
00621     hkey = u_strdup(rec->user);
00622     dbg_err_if (hkey == NULL);
00623 
00624     hobj = u_hmap_o_new((void *) hkey, (void *) rec);
00625     dbg_err_if (hobj == NULL);
00626 
00627     return u_hmap_put(pwd->db, hobj, NULL);
00628 err:
00629     if (hkey)
00630         u_free(hkey);
00631     if (hobj)
00632         u_hmap_o_free(hobj);
00633     return ~0;
00634 }
00635 
00636 /* 
00637  * prefabricated callbacks
00638  */
00639 static int __file_open (const char *path, void **pfp)
00640 {
00641     FILE *fp = NULL;
00642 
00643     dbg_err_sif ((fp = fopen(path, "r")) == NULL);
00644 
00645     *pfp = (void *) fp;
00646 
00647     return 0;
00648 err:
00649     return ~0;
00650 }
00651 
00652 static void __file_close (void *fp)
00653 {
00654     dbg_return_sif (fclose((FILE *) fp), /* nothing */);
00655     return;
00656 }
00657 
00658 static char *__file_load (char *str, int size, void *fp)
00659 {
00660     return fgets(str, size, (FILE *) fp);
00661 }
00662 
00663 /* in case file update has been detected, *pnew_update is also set */
00664 static int __file_notify (const char *path, time_t last_update, 
00665         time_t *pnew_update)
00666 {
00667     struct stat sb;
00668 
00669     dbg_err_if (path == NULL);
00670 
00671     dbg_err_sif (stat(path, &sb));
00672 
00673     if (sb.st_ctime != last_update)
00674     {
00675         *pnew_update = sb.st_ctime;
00676         return 1;
00677     }
00678 
00679     /* fall through (return false) */
00680 err:
00681     return 0;
00682 }
00683 
00684 /* hmap glue */
00685 static void __hmap_pwd_rec_free (u_hmap_o_t *obj)
00686 {
00687     u_pwd_t fake_pwd;
00688 
00689     fake_pwd.in_memory = 1;
00690 
00691     U_FREE(obj->key);
00692     u_pwd_rec_free(&fake_pwd, (u_pwd_rec_t *) obj->val);
00693 
00694     return;
00695 }

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