auth_tables.c

Go to the documentation of this file.
00001 /*
00002  * $Id$ 
00003  *
00004  * Copyright (c) 2007 iptelorg GmbH
00005  *
00006  * This file is part of SIP-router, a free SIP server.
00007  *
00008  * SIP-router is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version
00012  *
00013  * SIP-router is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  */
00022 
00030 #include <stdio.h>
00031 #include <time.h>
00032 #include <stdlib.h>
00033 #include <string.h>
00034 
00035 #include <openssl/pem.h>
00036 #include <openssl/err.h>
00037 #include <openssl/sha.h>
00038 
00039 #include "../../mem/shm_mem.h"
00040 #include "../../hashes.h"
00041 #include "auth_identity.h"
00042 
00043 #define lock_element(_cell)            lock_get(&((_cell)->lock))
00044 #define release_element(_cell)         lock_release(&((_cell)->lock))
00045 
00046 static int insert_into_table(ttable *ptable, void *pdata, unsigned int uhash);
00047 static void remove_from_table_unsafe(ttable *ptable, titem *pitem);
00048 static void remove_least(ttable *ptable, unsigned int uhash);
00049 static void* search_item_in_table_unsafe(ttable *ptable,
00050                                                                                  const void *pneedle,
00051                                                                                  unsigned int uhash);
00052 
00053 time_t glb_tnow=0;      /* we need for this for certificate expiration check when
00054                                          * we've to remove the least item from a table */
00055 
00056 int init_table(ttable **ptable, /* table we'd like to init */
00057                            unsigned int ubucknum,       /* number of buckets */
00058                            unsigned int uitemlim,       /* maximum number of table intems */
00059                            table_item_cmp *fcmp,        /* compare funcion used by search */
00060                            table_item_searchinit *fsinit, /* inits the least item searcher funcion */
00061                            table_item_cmp *fleast,      /* returns the less item;
00062                                                                                  * used by item remover */
00063                            table_item_free *ffree,      /* frees the data part of an item */
00064                            table_item_gc *fgc)  /* tells whether an item is garbage  */
00065 {
00066         int i1;
00067 
00068         if (!(*ptable = (ttable *) shm_malloc(sizeof(**ptable)))) {
00069                 LOG(L_ERR, "AUTH_IDENTITY:init_table: Not enough shared memory error\n");
00070                 return -1;
00071         }
00072         memset(*ptable, 0, sizeof(**ptable));
00073 
00074         if (!((*ptable)->entries = (tbucket *) shm_malloc(sizeof(tbucket)*ubucknum))) {
00075                 LOG(L_ERR, "AUTH_IDENTITY:init_table: Not enough shared memory error\n");
00076                 return -1;
00077         }
00078         memset((*ptable)->entries, 0, sizeof(tbucket)*ubucknum);
00079         for (i1=0; i1<ubucknum; i1++) {
00080                 (*ptable)->entries[i1].pfirst = NULL;
00081                 lock_init(&(*ptable)->entries[i1].lock);
00082         }
00083 
00084         (*ptable)->uitemlim=uitemlim;
00085         (*ptable)->ubuckets=ubucknum;
00086 
00087         (*ptable)->fcmp=fcmp;
00088         (*ptable)->fsearchinit=fsinit;
00089         (*ptable)->fleast=fleast;
00090         (*ptable)->ffree=ffree;
00091         (*ptable)->fgc=fgc;
00092 
00093         return 0;
00094 }
00095 
00096 void free_table(ttable *ptable)
00097 {
00098         unsigned int u1;
00099         titem *pitem, *previtem;
00100 
00101         if (ptable) {
00102                 for (u1=0; u1 < ptable->ubuckets; u1++)
00103                 {
00104                         pitem=ptable->entries[u1].pfirst;
00105                         while (pitem) {
00106                                 previtem=pitem;
00107                                 pitem=pitem->pnext;
00108 
00109                                 ptable->ffree(previtem->pdata);
00110                                 shm_free(previtem);
00111                         }
00112                 }
00113                 shm_free(ptable->entries);
00114                 shm_free(ptable);
00115         }
00116 }
00117 
00118 /* appends an item at the end of the bucket specified by uhash */
00119 static int insert_into_table(ttable *ptable, void *pdata, unsigned int uhash)
00120 {
00121         tbucket *pbucket;
00122         titem *pitem;
00123         char bneed2remove=0;
00124 
00125         if (!(pitem=(titem *)shm_malloc(sizeof(*pitem)))) {
00126                 LOG(L_ERR, "AUTH_IDENTITY:insert_into_table: Not enough shared memory error\n");
00127                 return -1;
00128         }
00129 
00130         memset(pitem, 0, sizeof(*pitem));
00131         pitem->uhash=uhash;
00132         pitem->pdata=pdata;
00133 
00134         lock_element(ptable);
00135         /* if there is not enough room for this item then we'll remove one */
00136         if (ptable->unum >= ptable->uitemlim)
00137                 bneed2remove=1;
00138         ptable->unum++;
00139         release_element(ptable);
00140 
00141         if (bneed2remove)
00142                 remove_least(ptable, uhash);
00143 
00144         /* locates the appropriate bucket */
00145         pbucket = &ptable->entries[uhash];
00146 
00147         /* insert into that bucket */
00148         lock_element(pbucket);
00149         if (pbucket->plast) {
00150                 pbucket->plast->pnext = pitem;
00151                 pitem->pprev = pbucket->plast;
00152         } else pbucket->pfirst = pitem;
00153         pbucket->plast = pitem;
00154         release_element(pbucket);
00155 
00156         return 0;
00157 }
00158 
00159 
00160 /*  Un-link a cell from hash_table */
00161 static void remove_from_table_unsafe(ttable *ptable, titem *pitem)
00162 {
00163         tbucket *pbucket = &(ptable->entries[pitem->uhash]);
00164 
00165         /* unlink the cell from entry list */
00166         if (pitem->pprev)
00167                 pitem->pprev->pnext = pitem->pnext;
00168         else
00169                 pbucket->pfirst = pitem->pnext;
00170 
00171         if (pitem->pnext)
00172                 pitem->pnext->pprev = pitem->pprev;
00173         else
00174                 pbucket->plast = pitem->pprev;
00175 
00176         if (ptable->ffree)
00177                 ptable->ffree(pitem->pdata);
00178 
00179         shm_free(pitem);
00180 }
00181 
00182 /* removes the least important item from its bucket or from the following first
00183    bucket which contains item */
00184 static void remove_least(ttable *ptable, unsigned int uhash)
00185 {
00186         tbucket *pbucket;
00187         unsigned int u1, uhashnow;
00188         titem *pleastitem=NULL, *pnow;
00189         int ires;
00190 
00191         if (!ptable->fleast)
00192                 return ;
00193         if (ptable->fsearchinit)
00194                 ptable->fsearchinit();
00195 
00196         for (uhashnow=uhash,u1=0, pbucket=&(ptable->entries[uhash]);
00197                  u1 < ptable->ubuckets;
00198                  u1++,pbucket=&(ptable->entries[uhashnow])) {
00199 
00200                 lock_element(pbucket);
00201                 /* if there any item in this bucket */
00202                 for (pnow=pbucket->pfirst;pnow;pnow=pnow->pnext) {
00203                         if (!pleastitem) {
00204                                 pleastitem=pnow;
00205                                 continue;
00206                         }
00207 
00208                         /*
00209                         fleast() return values:
00210                          1      s2 is less than s1
00211                          0      s1 and s2 are equal
00212                         -1  s1 is less than s2
00213                         -2      s1 is the least
00214                         -3  s2 is the least
00215                          */
00216                         ires=ptable->fleast(pleastitem->pdata, pnow->pdata);
00217                         if (ires==1)
00218                                 pleastitem=pnow;
00219                         if (ires==-2)
00220                                 break;
00221                         if (ires==-3) {
00222                                 pleastitem=pnow;
00223                                 break;
00224                         }
00225                 }
00226                 /* we found the least item in this bucket */
00227                 if (pleastitem) {
00228 
00229                         lock_element(ptable);
00230                         ptable->unum--;
00231                         release_element(ptable);
00232 
00233                         remove_from_table_unsafe(ptable, pleastitem);
00234                         release_element(pbucket);
00235                         return ;
00236                 }
00237                 release_element(pbucket);
00238 
00239 
00240                 /* we're in the last bucket so we start with the first one */
00241                 if (uhashnow + 1 == ptable->ubuckets)
00242                         uhashnow=0;
00243                 else
00244                 /* we step to the next bucket */
00245                         uhashnow++;
00246         }
00247 }
00248 
00249 /* looks for an item in the scepifiad bucket */
00250 static void* search_item_in_table_unsafe(ttable *ptable,
00251                                                                                  const void *pneedle,
00252                                                                                  unsigned int uhash)
00253 {
00254         tbucket *pbucket = &(ptable->entries[uhash]);
00255         titem *pnow;
00256         void *pret=NULL;
00257 
00258         if (!ptable->fcmp)
00259                 return NULL;
00260 
00261         for (pnow=pbucket->pfirst;pnow;pnow=pnow->pnext) {
00262                 if (!ptable->fcmp(pneedle, pnow->pdata)) {
00263                         pret=pnow->pdata;
00264                         break;
00265                 }
00266         }
00267 
00268         return pret;
00269 }
00270 
00271 /* looks for garbage in the hash interval specified by ihashstart and ihashend */
00272 void garbage_collect(ttable *ptable, int ihashstart, int ihashend)
00273 {
00274         unsigned int unum, uremoved;
00275         int i1;
00276         tbucket *pbucket;
00277         titem *pnow;
00278 
00279 
00280         /* there is not any garbage collector funcion available */
00281         if (!ptable->fgc)
00282                 return;
00283 
00284         if (ptable->fsearchinit)
00285                 ptable->fsearchinit();
00286 
00287         lock_element(ptable);
00288         unum=ptable->unum;
00289         release_element(ptable);
00290 
00291         /* if the half of the table is used or there is not so many items in a bucket
00292            then we return */
00293 //      if (unum < ptable->uitemlim/2 && unum < ptable->ubuckets*ITEM_IN_BUCKET_LIMIT)
00294 //              return ;
00295         if (!unum)
00296                 return ;
00297 
00298         for (i1=ihashstart; i1<=ihashend; i1++) {
00299                 uremoved=0;
00300                 pbucket=&(ptable->entries[i1]);
00301 
00302                 lock_element(pbucket);
00303                 for (pnow=pbucket->pfirst;pnow;pnow=pnow->pnext) {
00304                         if (ptable->fgc(pnow->pdata)) {
00305                                 remove_from_table_unsafe(ptable, pnow);
00306                                 uremoved++;
00307                         }
00308                 }
00309                 /* if we removed any item from table then we would update the item counter */
00310                 if (uremoved) {
00311                         lock_element(ptable);
00312                         ptable->unum-=uremoved;
00313                         release_element(ptable);
00314                 }
00315                 release_element(pbucket);
00316         }
00317 }
00318 
00319 
00320 /*
00321  * Make a copy of a str structure using shm_malloc
00322  */
00323 static int str_duplicate(str* _d, str* _s)
00324 {
00325 
00326         _d->s = (char *)shm_malloc(sizeof(char)*(_s->len));
00327         if (!_d->s) {
00328                 LOG(L_ERR, "AUTH_IDENTITY:str_duplicate: No enough shared memory\n");
00329                 return -1;
00330         }
00331 
00332         memcpy(_d->s, _s->s, _s->len);
00333         _d->len = _s->len;
00334         return 0;
00335 }
00336 
00337 /*
00338  *
00339  * Certificate table specific funcions
00340  *
00341  */
00342 int cert_item_cmp(const void *s1, const void *s2)
00343 {
00344         tcert_item *p1=(tcert_item*)s1, *p2=(tcert_item*)s2;
00345 
00346         return !(p1->surl.len==p2->surl.len && !memcmp(p1->surl.s, p2->surl.s, p2->surl.len));
00347 }
00348 
00349 void cert_item_init()
00350 {
00351         /* we need for this for certificate expiration check when
00352          * we've to remove an item from the table */
00353         glb_tnow=time(0);
00354 }
00355 
00356 /* we remove a certificate if expired or if accessed less than an other */
00357 int cert_item_least(const void *s1, const void *s2)
00358 {
00359         if (((tcert_item *)s1)->ivalidbefore < glb_tnow)
00360                 return -2;
00361         if (((tcert_item *)s2)->ivalidbefore < glb_tnow)
00362                 return -3;
00363         return (((tcert_item *)s1)->uaccessed < ((tcert_item *)s2)->uaccessed) ? -1 : 1;
00364 }
00365 
00366 /* frees a certificate item */
00367 void cert_item_free(const void *sitem)
00368 {
00369         shm_free(((tcert_item *)sitem)->surl.s);
00370         shm_free(((tcert_item *)sitem)->scertpem.s);
00371         shm_free((tcert_item *)sitem);
00372 }
00373 
00374 /* looks for a certificate in a table and increases access counter of that
00375    table item */
00376 int get_cert_from_table(ttable *ptable, str *skey, tcert_item *ptarget)
00377 {
00378         tcert_item* tmp_tcert_item;
00379         unsigned int uhash;
00380         int iret=0;
00381 
00382         uhash=get_hash1_raw(skey->s, skey->len) & (CERTIFICATE_TABLE_ENTRIES-1);
00383 
00384         /* we lock the whole bucket */
00385         lock_element(&ptable->entries[uhash]);
00386 
00387         tmp_tcert_item = search_item_in_table_unsafe(ptable,
00388                                                                                                  (const void *)skey,
00389                                                                                                  uhash);
00390         /* make a copy of found certificate and after the certificate
00391          * verification we'll add it to certificate table */
00392         if (tmp_tcert_item) {
00393                 memcpy(ptarget->scertpem.s, tmp_tcert_item->scertpem.s, tmp_tcert_item->scertpem.len);
00394                 ptarget->scertpem.len=tmp_tcert_item->scertpem.len;
00395                 /* we accessed this certificate */
00396                 tmp_tcert_item->uaccessed++;
00397         }
00398         else
00399                 iret=1;
00400 
00401         release_element(&ptable->entries[uhash]);
00402 
00403         return iret;
00404 }
00405 
00406 /* inserts an item to table, and removes the least item if the table is full */
00407 int addcert2table(ttable *ptable, tcert_item *pcert)
00408 {
00409         tcert_item *pshmcert;
00410         unsigned int uhash;
00411 
00412         if (!(pshmcert=(tcert_item *)shm_malloc(sizeof(*pshmcert)))) {
00413                 LOG(L_ERR, "AUTH_IDENTITY:addcert2table: No enough shared memory\n");
00414                 return -1;
00415         }
00416         memset(pshmcert, 0, sizeof(*pshmcert));
00417         if (str_duplicate(&pshmcert->surl, &pcert->surl))
00418                 return -2;
00419 
00420         if (str_duplicate(&pshmcert->scertpem, &pcert->scertpem))
00421                 return -3;
00422 
00423         pshmcert->ivalidbefore=pcert->ivalidbefore;
00424         pshmcert->uaccessed=1;
00425 
00426         uhash=get_hash1_raw(pcert->surl.s, pcert->surl.len) & (CERTIFICATE_TABLE_ENTRIES-1);
00427 
00428         if (insert_into_table(ptable, (void*)pshmcert, uhash))
00429                 return -4;
00430 
00431         return 0;
00432 }
00433 
00434 /*
00435  *
00436  * Call-ID table specific funcions
00437  *
00438  */
00439 
00440 int cid_item_cmp(const void *s1, const void *s2)
00441 {
00442         tcid_item *p1=(tcid_item*)s1, *p2=(tcid_item*)s2;
00443 
00444         return !(p1->scid.len==p2->scid.len && !memcmp(p1->scid.s, p2->scid.s, p2->scid.len));
00445 }
00446 
00447 void cid_item_init()
00448 {
00449         glb_tnow=time(0);
00450 }
00451 
00452 /* we remove a call-id if older than an other */
00453 int cid_item_least(const void *s1, const void *s2)
00454 {
00455         if (((tcid_item *)s1)->ivalidbefore < glb_tnow)
00456                 return -2;
00457         if (((tcid_item *)s2)->ivalidbefore < glb_tnow)
00458                 return -3;
00459 
00460         return (((tcid_item *)s1)->ivalidbefore < ((tcid_item *)s2)->ivalidbefore) ? -1 : 1;
00461 }
00462 
00463 /* tells whether an item is garbage */
00464 int cid_item_gc(const void *s1)
00465 {
00466         return (((tcid_item *)s1)->ivalidbefore < glb_tnow);
00467 }
00468 
00469 /* frees a call-id item */
00470 void cid_item_free(const void *sitem)
00471 {
00472         tcid_item *pcid=(tcid_item *)sitem;
00473         tdlg_item *pdlgs, *pdlgs_next;
00474 
00475         shm_free(pcid->scid.s);
00476 
00477         pdlgs_next=pcid->pdlgs;
00478         while (pdlgs_next) {
00479                 pdlgs=pdlgs_next;
00480                 pdlgs_next=pdlgs_next->pnext;
00481                 shm_free (pdlgs->sftag.s);
00482                 shm_free (pdlgs);
00483         }
00484 
00485         shm_free((tcert_item *)sitem);
00486 }
00487 
00488 /* inserts a callid item to table, and removes the least item if the table is full */
00489 int proc_cid(ttable *ptable,
00490                          str *scid,
00491                          str *sftag,
00492                          unsigned int ucseq,
00493                          time_t ivalidbefore)
00494 {
00495         tcid_item *pshmcid, *pcid_item;
00496         tdlg_item *pshmdlg, *pdlg_item, *pdlg_item_prev;
00497         unsigned int uhash;
00498 
00499         /* we suppose that this SIP request is not replayed so it doesn't exist in
00500            the table so we prepare to insert */
00501         if (!(pshmdlg=(tdlg_item *)shm_malloc(sizeof(*pshmdlg)))) {
00502                 LOG(L_ERR, "AUTH_IDENTITY:addcid2table: No enough shared memory\n");
00503                 return -1;
00504         }
00505         memset(pshmdlg, 0, sizeof(*pshmdlg));
00506         if (str_duplicate(&pshmdlg->sftag, sftag))
00507                 return -2;
00508         pshmdlg->ucseq=ucseq;
00509 
00510 
00511         /* we're looking for this call-id item if exists */
00512         uhash=get_hash1_raw(scid->s, scid->len) & (CALLID_TABLE_ENTRIES-1);
00513 
00514         lock_element(&ptable->entries[uhash]);
00515 
00516         pcid_item = search_item_in_table_unsafe(ptable,
00517                                                                                         (const void *)scid, /* Call-id is the key */
00518                                                                                         uhash);
00519         /* we've found one call-id so we're looking for the required SIP request */
00520         if (pcid_item) {
00521                 for (pdlg_item=pcid_item->pdlgs, pdlg_item_prev=NULL;
00522                      pdlg_item;
00523                          pdlg_item=pdlg_item->pnext) {
00524                         if (pdlg_item->sftag.len==sftag->len
00525                                 && !memcmp(pdlg_item->sftag.s, sftag->s, sftag->len)) {
00526                                 /* we found this call with this from tag */
00527                                 if (pdlg_item->ucseq>=ucseq) {
00528                                         /* we've found this or older request in the table!
00529                                            this call is replayed! */
00530                                         release_element(&ptable->entries[uhash]);
00531 
00532                                         shm_free(pshmdlg->sftag.s);
00533                                         shm_free(pshmdlg);
00534                                         return AUTH_FOUND;
00535                                 } else {
00536                                         /* this is another later request whithin this dialog so we
00537                                            update the saved cseq */
00538                                         pdlg_item->ucseq=ucseq;
00539                                         release_element(&ptable->entries[uhash]);
00540 
00541                                         shm_free(pshmdlg->sftag.s);
00542                                         shm_free(pshmdlg);
00543                                         return 0;
00544                                 }
00545                         }
00546                         /* we save the previous dialog item in order to append a new item more easily */
00547                         pdlg_item_prev ?
00548                                 (pdlg_item_prev=pdlg_item_prev->pnext) :
00549                                 (pdlg_item_prev=pdlg_item);
00550                 }
00551                 /* we append this to item dialogs*/
00552                 pdlg_item_prev->pnext=pshmdlg;
00553                 /* this is the latest request; we hold all request concerned this
00554                    call-id until the latest request is valid */
00555                 pcid_item->ivalidbefore=ivalidbefore;
00556         }
00557 
00558         release_element(&ptable->entries[uhash]);
00559 
00560         if (!pcid_item) {
00561                 /* this is the first request with this call-id */
00562                 if (!(pshmcid=(tcid_item *)shm_malloc(sizeof(*pshmcid)))) {
00563                         LOG(L_ERR, "AUTH_IDENTITY:addcid2table: No enough shared memory\n");
00564                         return -4;
00565                 }
00566                 memset(pshmcid, 0, sizeof(*pshmcid));
00567                 if (str_duplicate(&pshmcid->scid, scid)) {
00568                         return -5;
00569                 }
00570                 pshmcid->ivalidbefore=ivalidbefore;
00571                 pshmcid->pdlgs=pshmdlg;
00572                 if (insert_into_table(ptable, (void*)pshmcid, uhash))
00573                         return -6;
00574         }
00575 
00576         return 0;
00577 }