1 /*
   2 ** Copyright (c) 1999-2002 Proofpoint, Inc. and its suppliers.
   3 **      All rights reserved.
   4 **
   5 ** By using this file, you agree to the terms and conditions set
   6 ** forth in the LICENSE file which can be found at the top level of
   7 ** the sendmail distribution.
   8 */
   9 
  10 #include <sm/gen.h>
  11 SM_RCSID("@(#)$Id: smndbm.c,v 8.55 2013-11-22 20:51:49 ca Exp $")
  12 
  13 #include <fcntl.h>
  14 #include <stdlib.h>
  15 #include <unistd.h>
  16 
  17 #include <sendmail/sendmail.h>
  18 #include <libsmdb/smdb.h>
  19 
  20 #ifdef NDBM
  21 
  22 # define SMNDB_DIR_FILE_EXTENSION "dir"
  23 # define SMNDB_PAG_FILE_EXTENSION "pag"
  24 
  25 struct smdb_dbm_database_struct
  26 {
  27         DBM     *smndbm_dbm;
  28         int     smndbm_lock_fd;
  29         bool    smndbm_cursor_in_use;
  30 };
  31 typedef struct smdb_dbm_database_struct SMDB_DBM_DATABASE;
  32 
  33 struct smdb_dbm_cursor_struct
  34 {
  35         SMDB_DBM_DATABASE       *smndbmc_db;
  36         datum                   smndbmc_current_key;
  37 };
  38 typedef struct smdb_dbm_cursor_struct SMDB_DBM_CURSOR;
  39 
  40 /*
  41 **  SMDB_PUT_FLAGS_TO_NDBM_FLAGS -- Translates smdb put flags to ndbm put flags.
  42 **
  43 **      Parameters:
  44 **              flags -- The flags to translate.
  45 **
  46 **      Returns:
  47 **              The ndbm flags that are equivalent to the smdb flags.
  48 **
  49 **      Notes:
  50 **              Any invalid flags are ignored.
  51 **
  52 */
  53 
  54 int
  55 smdb_put_flags_to_ndbm_flags(flags)
  56         SMDB_FLAG flags;
  57 {
  58         int return_flags;
  59 
  60         return_flags = 0;
  61         if (bitset(SMDBF_NO_OVERWRITE, flags))
  62                 return_flags = DBM_INSERT;
  63         else
  64                 return_flags = DBM_REPLACE;
  65 
  66         return return_flags;
  67 }
  68 
  69 /*
  70 **  Except for smdb_ndbm_open, the rest of these function correspond to the
  71 **  interface laid out in smdb.h.
  72 */
  73 
  74 SMDB_DBM_DATABASE *
  75 smdbm_malloc_database()
  76 {
  77         SMDB_DBM_DATABASE *db;
  78 
  79         db = (SMDB_DBM_DATABASE *) malloc(sizeof(SMDB_DBM_DATABASE));
  80         if (db != NULL)
  81         {
  82                 db->smndbm_dbm = NULL;
  83                 db->smndbm_lock_fd = -1;
  84                 db->smndbm_cursor_in_use = false;
  85         }
  86 
  87         return db;
  88 }
  89 
  90 int
  91 smdbm_close(database)
  92         SMDB_DATABASE *database;
  93 {
  94         SMDB_DBM_DATABASE *db = (SMDB_DBM_DATABASE *) database->smdb_impl;
  95         DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
  96 
  97         dbm_close(dbm);
  98         if (db->smndbm_lock_fd != -1)
  99                 close(db->smndbm_lock_fd);
 100 
 101         free(db);
 102         database->smdb_impl = NULL;
 103 
 104         return SMDBE_OK;
 105 }
 106 
 107 int
 108 smdbm_del(database, key, flags)
 109         SMDB_DATABASE *database;
 110         SMDB_DBENT *key;
 111         unsigned int flags;
 112 {
 113         int result;
 114         DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
 115         datum dbkey;
 116 
 117         (void) memset(&dbkey, '\0', sizeof dbkey);
 118         dbkey.dptr = key->data;
 119         dbkey.dsize = key->size;
 120 
 121         errno = 0;
 122         result = dbm_delete(dbm, dbkey);
 123         if (result != 0)
 124         {
 125                 int save_errno = errno;
 126 
 127                 if (dbm_error(dbm))
 128                         return SMDBE_IO_ERROR;
 129 
 130                 if (save_errno != 0)
 131                         return save_errno;
 132 
 133                 return SMDBE_NOT_FOUND;
 134         }
 135         return SMDBE_OK;
 136 }
 137 
 138 int
 139 smdbm_fd(database, fd)
 140         SMDB_DATABASE *database;
 141         int *fd;
 142 {
 143         DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
 144 
 145         *fd = dbm_dirfno(dbm);
 146         if (*fd <= 0)
 147                 return EINVAL;
 148 
 149         return SMDBE_OK;
 150 }
 151 
 152 int
 153 smdbm_lockfd(database)
 154         SMDB_DATABASE *database;
 155 {
 156         SMDB_DBM_DATABASE *db = (SMDB_DBM_DATABASE *) database->smdb_impl;
 157 
 158         return db->smndbm_lock_fd;
 159 }
 160 
 161 int
 162 smdbm_get(database, key, data, flags)
 163         SMDB_DATABASE *database;
 164         SMDB_DBENT *key;
 165         SMDB_DBENT *data;
 166         unsigned int flags;
 167 {
 168         DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
 169         datum dbkey, dbdata;
 170 
 171         (void) memset(&dbkey, '\0', sizeof dbkey);
 172         (void) memset(&dbdata, '\0', sizeof dbdata);
 173         dbkey.dptr = key->data;
 174         dbkey.dsize = key->size;
 175 
 176         errno = 0;
 177         dbdata = dbm_fetch(dbm, dbkey);
 178         if (dbdata.dptr == NULL)
 179         {
 180                 int save_errno = errno;
 181 
 182                 if (dbm_error(dbm))
 183                         return SMDBE_IO_ERROR;
 184 
 185                 if (save_errno != 0)
 186                         return save_errno;
 187 
 188                 return SMDBE_NOT_FOUND;
 189         }
 190         data->data = dbdata.dptr;
 191         data->size = dbdata.dsize;
 192         return SMDBE_OK;
 193 }
 194 
 195 int
 196 smdbm_put(database, key, data, flags)
 197         SMDB_DATABASE *database;
 198         SMDB_DBENT *key;
 199         SMDB_DBENT *data;
 200         unsigned int flags;
 201 {
 202         int result;
 203         int save_errno;
 204         DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
 205         datum dbkey, dbdata;
 206 
 207         (void) memset(&dbkey, '\0', sizeof dbkey);
 208         (void) memset(&dbdata, '\0', sizeof dbdata);
 209         dbkey.dptr = key->data;
 210         dbkey.dsize = key->size;
 211         dbdata.dptr = data->data;
 212         dbdata.dsize = data->size;
 213 
 214         errno = 0;
 215         result = dbm_store(dbm, dbkey, dbdata,
 216                            smdb_put_flags_to_ndbm_flags(flags));
 217         switch (result)
 218         {
 219           case 1:
 220                 return SMDBE_DUPLICATE;
 221 
 222           case 0:
 223                 return SMDBE_OK;
 224 
 225           default:
 226                 save_errno = errno;
 227 
 228                 if (dbm_error(dbm))
 229                         return SMDBE_IO_ERROR;
 230 
 231                 if (save_errno != 0)
 232                         return save_errno;
 233 
 234                 return SMDBE_IO_ERROR;
 235         }
 236         /* NOTREACHED */
 237 }
 238 
 239 int
 240 smndbm_set_owner(database, uid, gid)
 241         SMDB_DATABASE *database;
 242         uid_t uid;
 243         gid_t gid;
 244 {
 245 # if HASFCHOWN
 246         int fd;
 247         int result;
 248         DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
 249 
 250         fd = dbm_dirfno(dbm);
 251         if (fd <= 0)
 252                 return EINVAL;
 253 
 254         result = fchown(fd, uid, gid);
 255         if (result < 0)
 256                 return errno;
 257 
 258         fd = dbm_pagfno(dbm);
 259         if (fd <= 0)
 260                 return EINVAL;
 261 
 262         result = fchown(fd, uid, gid);
 263         if (result < 0)
 264                 return errno;
 265 # endif /* HASFCHOWN */
 266 
 267         return SMDBE_OK;
 268 }
 269 
 270 int
 271 smdbm_sync(database, flags)
 272         SMDB_DATABASE *database;
 273         unsigned int flags;
 274 {
 275         return SMDBE_UNSUPPORTED;
 276 }
 277 
 278 int
 279 smdbm_cursor_close(cursor)
 280         SMDB_CURSOR *cursor;
 281 {
 282         SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl;
 283         SMDB_DBM_DATABASE *db = dbm_cursor->smndbmc_db;
 284 
 285         if (!db->smndbm_cursor_in_use)
 286                 return SMDBE_NOT_A_VALID_CURSOR;
 287 
 288         db->smndbm_cursor_in_use = false;
 289         free(dbm_cursor);
 290         free(cursor);
 291 
 292         return SMDBE_OK;
 293 }
 294 
 295 int
 296 smdbm_cursor_del(cursor, flags)
 297         SMDB_CURSOR *cursor;
 298         unsigned int flags;
 299 {
 300         int result;
 301         SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl;
 302         SMDB_DBM_DATABASE *db = dbm_cursor->smndbmc_db;
 303         DBM *dbm = db->smndbm_dbm;
 304 
 305         errno = 0;
 306         result = dbm_delete(dbm, dbm_cursor->smndbmc_current_key);
 307         if (result != 0)
 308         {
 309                 int save_errno = errno;
 310 
 311                 if (dbm_error(dbm))
 312                         return SMDBE_IO_ERROR;
 313 
 314                 if (save_errno != 0)
 315                         return save_errno;
 316 
 317                 return SMDBE_NOT_FOUND;
 318         }
 319         return SMDBE_OK;
 320 }
 321 
 322 int
 323 smdbm_cursor_get(cursor, key, value, flags)
 324         SMDB_CURSOR *cursor;
 325         SMDB_DBENT *key;
 326         SMDB_DBENT *value;
 327         SMDB_FLAG flags;
 328 {
 329         SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl;
 330         SMDB_DBM_DATABASE *db = dbm_cursor->smndbmc_db;
 331         DBM *dbm = db->smndbm_dbm;
 332         datum dbkey, dbdata;
 333 
 334         (void) memset(&dbkey, '\0', sizeof dbkey);
 335         (void) memset(&dbdata, '\0', sizeof dbdata);
 336 
 337         if (flags == SMDB_CURSOR_GET_RANGE)
 338                 return SMDBE_UNSUPPORTED;
 339 
 340         if (dbm_cursor->smndbmc_current_key.dptr == NULL)
 341         {
 342                 dbm_cursor->smndbmc_current_key = dbm_firstkey(dbm);
 343                 if (dbm_cursor->smndbmc_current_key.dptr == NULL)
 344                 {
 345                         if (dbm_error(dbm))
 346                                 return SMDBE_IO_ERROR;
 347                         return SMDBE_LAST_ENTRY;
 348                 }
 349         }
 350         else
 351         {
 352                 dbm_cursor->smndbmc_current_key = dbm_nextkey(dbm);
 353                 if (dbm_cursor->smndbmc_current_key.dptr == NULL)
 354                 {
 355                         if (dbm_error(dbm))
 356                                 return SMDBE_IO_ERROR;
 357                         return SMDBE_LAST_ENTRY;
 358                 }
 359         }
 360 
 361         errno = 0;
 362         dbdata = dbm_fetch(dbm, dbm_cursor->smndbmc_current_key);
 363         if (dbdata.dptr == NULL)
 364         {
 365                 int save_errno = errno;
 366 
 367                 if (dbm_error(dbm))
 368                         return SMDBE_IO_ERROR;
 369 
 370                 if (save_errno != 0)
 371                         return save_errno;
 372 
 373                 return SMDBE_NOT_FOUND;
 374         }
 375         value->data = dbdata.dptr;
 376         value->size = dbdata.dsize;
 377         key->data = dbm_cursor->smndbmc_current_key.dptr;
 378         key->size = dbm_cursor->smndbmc_current_key.dsize;
 379 
 380         return SMDBE_OK;
 381 }
 382 
 383 int
 384 smdbm_cursor_put(cursor, key, value, flags)
 385         SMDB_CURSOR *cursor;
 386         SMDB_DBENT *key;
 387         SMDB_DBENT *value;
 388         SMDB_FLAG flags;
 389 {
 390         int result;
 391         int save_errno;
 392         SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl;
 393         SMDB_DBM_DATABASE *db = dbm_cursor->smndbmc_db;
 394         DBM *dbm = db->smndbm_dbm;
 395         datum dbdata;
 396 
 397         (void) memset(&dbdata, '\0', sizeof dbdata);
 398         dbdata.dptr = value->data;
 399         dbdata.dsize = value->size;
 400 
 401         errno = 0;
 402         result = dbm_store(dbm, dbm_cursor->smndbmc_current_key, dbdata,
 403                            smdb_put_flags_to_ndbm_flags(flags));
 404         switch (result)
 405         {
 406           case 1:
 407                 return SMDBE_DUPLICATE;
 408 
 409           case 0:
 410                 return SMDBE_OK;
 411 
 412           default:
 413                 save_errno = errno;
 414 
 415                 if (dbm_error(dbm))
 416                         return SMDBE_IO_ERROR;
 417 
 418                 if (save_errno != 0)
 419                         return save_errno;
 420 
 421                 return SMDBE_IO_ERROR;
 422         }
 423         /* NOTREACHED */
 424 }
 425 
 426 int
 427 smdbm_cursor(database, cursor, flags)
 428         SMDB_DATABASE *database;
 429         SMDB_CURSOR **cursor;
 430         SMDB_FLAG flags;
 431 {
 432         SMDB_DBM_DATABASE *db = (SMDB_DBM_DATABASE *) database->smdb_impl;
 433         SMDB_CURSOR *cur;
 434         SMDB_DBM_CURSOR *dbm_cursor;
 435 
 436         if (db->smndbm_cursor_in_use)
 437                 return SMDBE_ONLY_SUPPORTS_ONE_CURSOR;
 438 
 439         db->smndbm_cursor_in_use = true;
 440         dbm_cursor = (SMDB_DBM_CURSOR *) malloc(sizeof(SMDB_DBM_CURSOR));
 441         if (dbm_cursor == NULL)
 442                 return SMDBE_MALLOC;
 443         dbm_cursor->smndbmc_db = db;
 444         dbm_cursor->smndbmc_current_key.dptr = NULL;
 445         dbm_cursor->smndbmc_current_key.dsize = 0;
 446 
 447         cur = (SMDB_CURSOR*) malloc(sizeof(SMDB_CURSOR));
 448         if (cur == NULL)
 449         {
 450                 free(dbm_cursor);
 451                 return SMDBE_MALLOC;
 452         }
 453 
 454         cur->smdbc_impl = dbm_cursor;
 455         cur->smdbc_close = smdbm_cursor_close;
 456         cur->smdbc_del = smdbm_cursor_del;
 457         cur->smdbc_get = smdbm_cursor_get;
 458         cur->smdbc_put = smdbm_cursor_put;
 459         *cursor = cur;
 460 
 461         return SMDBE_OK;
 462 }
 463 /*
 464 **  SMDB_NDBM_OPEN -- Opens a ndbm database.
 465 **
 466 **      Parameters:
 467 **              database -- An unallocated database pointer to a pointer.
 468 **              db_name -- The name of the database without extension.
 469 **              mode -- File permisions on a created database.
 470 **              mode_mask -- Mode bits that much match on an opened database.
 471 **              sff -- Flags to safefile.
 472 **              type -- The type of database to open.
 473 **                      Only SMDB_NDBM is supported.
 474 **              user_info -- Information on the user to use for file
 475 **                          permissions.
 476 **              db_params -- No params are supported.
 477 **
 478 **      Returns:
 479 **              SMDBE_OK -- Success, otherwise errno:
 480 **              SMDBE_MALLOC -- Cannot allocate memory.
 481 **              SMDBE_UNSUPPORTED -- The type is not supported.
 482 **              SMDBE_GDBM_IS_BAD -- We have detected GDBM and we don't
 483 **                                  like it.
 484 **              SMDBE_BAD_OPEN -- dbm_open failed and errno was not set.
 485 **              Anything else: errno
 486 */
 487 
 488 int
 489 smdb_ndbm_open(database, db_name, mode, mode_mask, sff, type, user_info,
 490                db_params)
 491         SMDB_DATABASE **database;
 492         char *db_name;
 493         int mode;
 494         int mode_mask;
 495         long sff;
 496         SMDB_DBTYPE type;
 497         SMDB_USER_INFO *user_info;
 498         SMDB_DBPARAMS *db_params;
 499 {
 500         bool lockcreated = false;
 501         int result;
 502         int lock_fd;
 503         SMDB_DATABASE *smdb_db;
 504         SMDB_DBM_DATABASE *db;
 505         DBM *dbm = NULL;
 506         struct stat dir_stat_info;
 507         struct stat pag_stat_info;
 508 
 509         result = SMDBE_OK;
 510         *database = NULL;
 511 
 512         if (type == NULL)
 513                 return SMDBE_UNKNOWN_DB_TYPE;
 514 
 515         result = smdb_setup_file(db_name, SMNDB_DIR_FILE_EXTENSION, mode_mask,
 516                                  sff, user_info, &dir_stat_info);
 517         if (result != SMDBE_OK)
 518                 return result;
 519 
 520         result = smdb_setup_file(db_name, SMNDB_PAG_FILE_EXTENSION, mode_mask,
 521                                  sff, user_info, &pag_stat_info);
 522         if (result != SMDBE_OK)
 523                 return result;
 524 
 525         if ((dir_stat_info.st_mode == ST_MODE_NOFILE ||
 526              pag_stat_info.st_mode == ST_MODE_NOFILE) &&
 527             bitset(mode, O_CREAT))
 528                 lockcreated = true;
 529 
 530         lock_fd = -1;
 531         result = smdb_lock_file(&lock_fd, db_name, mode, sff,
 532                                 SMNDB_DIR_FILE_EXTENSION);
 533         if (result != SMDBE_OK)
 534                 return result;
 535 
 536         if (lockcreated)
 537         {
 538                 int pag_fd;
 539 
 540                 /* Need to pre-open the .pag file as well with O_EXCL */
 541                 result = smdb_lock_file(&pag_fd, db_name, mode, sff,
 542                                         SMNDB_PAG_FILE_EXTENSION);
 543                 if (result != SMDBE_OK)
 544                 {
 545                         (void) close(lock_fd);
 546                         return result;
 547                 }
 548                 (void) close(pag_fd);
 549 
 550                 mode |= O_TRUNC;
 551                 mode &= ~(O_CREAT|O_EXCL);
 552         }
 553 
 554         smdb_db = smdb_malloc_database();
 555         if (smdb_db == NULL)
 556                 result = SMDBE_MALLOC;
 557 
 558         db = smdbm_malloc_database();
 559         if (db == NULL)
 560                 result = SMDBE_MALLOC;
 561 
 562         /* Try to open database */
 563         if (result == SMDBE_OK)
 564         {
 565                 db->smndbm_lock_fd = lock_fd;
 566 
 567                 errno = 0;
 568                 dbm = dbm_open(db_name, mode, DBMMODE);
 569                 if (dbm == NULL)
 570                 {
 571                         if (errno == 0)
 572                                 result = SMDBE_BAD_OPEN;
 573                         else
 574                                 result = errno;
 575                 }
 576                 db->smndbm_dbm = dbm;
 577         }
 578 
 579         /* Check for GDBM */
 580         if (result == SMDBE_OK)
 581         {
 582                 if (dbm_dirfno(dbm) == dbm_pagfno(dbm))
 583                         result = SMDBE_GDBM_IS_BAD;
 584         }
 585 
 586         /* Check for filechanged */
 587         if (result == SMDBE_OK)
 588         {
 589                 result = smdb_filechanged(db_name, SMNDB_DIR_FILE_EXTENSION,
 590                                           dbm_dirfno(dbm), &dir_stat_info);
 591                 if (result == SMDBE_OK)
 592                 {
 593                         result = smdb_filechanged(db_name,
 594                                                   SMNDB_PAG_FILE_EXTENSION,
 595                                                   dbm_pagfno(dbm),
 596                                                   &pag_stat_info);
 597                 }
 598         }
 599 
 600         /* XXX Got to get fchown stuff in here */
 601 
 602         /* Setup driver if everything is ok */
 603         if (result == SMDBE_OK)
 604         {
 605                 *database = smdb_db;
 606 
 607                 smdb_db->smdb_close = smdbm_close;
 608                 smdb_db->smdb_del = smdbm_del;
 609                 smdb_db->smdb_fd = smdbm_fd;
 610                 smdb_db->smdb_lockfd = smdbm_lockfd;
 611                 smdb_db->smdb_get = smdbm_get;
 612                 smdb_db->smdb_put = smdbm_put;
 613                 smdb_db->smdb_set_owner = smndbm_set_owner;
 614                 smdb_db->smdb_sync = smdbm_sync;
 615                 smdb_db->smdb_cursor = smdbm_cursor;
 616 
 617                 smdb_db->smdb_impl = db;
 618 
 619                 return SMDBE_OK;
 620         }
 621 
 622         /* If we're here, something bad happened, clean up */
 623         if (dbm != NULL)
 624                 dbm_close(dbm);
 625 
 626         smdb_unlock_file(db->smndbm_lock_fd);
 627         free(db);
 628         smdb_free_database(smdb_db);
 629 
 630         return result;
 631 }
 632 #endif /* NDBM */