1   /*
   2    * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
   3    * Use is subject to license terms.
   4    */
   5   
   6 | #pragma ident        "@(#)kdb_db2.c        1.4        04/09/08 SMI"
   6 | #pragma ident        "@(#)kdb_db2.c        1.3        04/02/20 SMI"
   7   
   8   /*
   9    * lib/kdb/kdb_db2.c
  10    *
  11    * Copyright 1997 by the Massachusetts Institute of Technology.
  12    * All Rights Reserved.
  13    *
  14    * Export of this software from the United States of America may
  15    *   require a specific license from the United States Government.
  16    *   It is the responsibility of any person or organization contemplating
  17    *   export to obtain such a license before exporting.
  18    * 
  19    * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
  20    * distribute this software and its documentation for any purpose and
  21    * without fee is hereby granted, provided that the above copyright
  22    * notice appear in all copies and that both that copyright notice and
  23    * this permission notice appear in supporting documentation, and that
  24    * the name of M.I.T. not be used in advertising or publicity pertaining
  25    * to distribution of the software without specific, written prior
  26    * permission.  Furthermore if you modify this software you must label
  27    * your software as modified software and not distribute it in such a
  28    * fashion that it might be confused with the original M.I.T. software.
  29    * M.I.T. makes no representations about the suitability of
  30    * this software for any purpose.  It is provided "as is" without express
  31    * or implied warranty.
  32    * 
  33    */
  34   
  35   /*
  36    * Copyright (C) 1998 by the FundsXpress, INC.
  37    * 
  38    * All rights reserved.
  39    * 
  40    * Export of this software from the United States of America may require
  41    * a specific license from the United States Government.  It is the
  42    * responsibility of any person or organization contemplating export to
  43    * obtain such a license before exporting.
  44    * 
  45    * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
  46    * distribute this software and its documentation for any purpose and
  47    * without fee is hereby granted, provided that the above copyright
  48    * notice appear in all copies and that both that copyright notice and
  49    * this permission notice appear in supporting documentation, and that
  50    * the name of FundsXpress. not be used in advertising or publicity pertaining
  51    * to distribution of the software without specific, written prior
  52    * permission.  FundsXpress makes no representations about the suitability of
  53    * this software for any purpose.  It is provided "as is" without express
  54    * or implied warranty.
  55    * 
  56    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  57    * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  58    * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  59    */
  60   
  61   #if HAVE_UNISTD_H
  62   #include <unistd.h>
  63   #endif
  64   
  65   #include "k5-int.h"
  66 + #include "kdb_log.h"
  67   #include <db.h>
  68   #include <stdio.h>
  69   #include <errno.h>
  70   #include <utime.h>
  71   
  72   #define OLD_COMPAT_VERSION_1
  73   
  74   #ifdef OLD_COMPAT_VERSION_1
  75   #include "kdb_compat.h"
  76   #endif
  77   
  78   #include "kdb_db2.h"
  79   
  80   static char *gen_dbsuffix 
  81           PROTOTYPE((char *, char * ));
  82   static krb5_error_code krb5_db2_db_start_update 
  83           PROTOTYPE((krb5_context));
  84   static krb5_error_code krb5_db2_db_end_update 
  85           PROTOTYPE((krb5_context));
  86   static krb5_error_code krb5_db2_db_set_hashfirst
  87           PROTOTYPE((krb5_context, int));
  88   
  89   static char default_db_name[] = DEFAULT_KDB_FILE;
  90   
  91   /*
  92    * Locking:
  93    * 
  94    * There are two distinct locking protocols used.  One is designed to
  95    * lock against processes (the admin_server, for one) which make
  96    * incremental changes to the database; the other is designed to lock
  97    * against utilities (kdb5_edit, kpropd, kdb5_convert) which replace the
  98    * entire database in one fell swoop.
  99    *
 100    * The first locking protocol is implemented using flock() in the 
 101    * krb_dbl_lock() and krb_dbl_unlock routines.
 102    *
 103    * The second locking protocol is necessary because DBM "files" are
 104    * actually implemented as two separate files, and it is impossible to
 105    * atomically rename two files simultaneously.  It assumes that the
 106    * database is replaced only very infrequently in comparison to the time
 107    * needed to do a database read operation.
 108    *
 109    * A third file is used as a "version" semaphore; the modification
 110    * time of this file is the "version number" of the database.
 111    * At the start of a read operation, the reader checks the version
 112    * number; at the end of the read operation, it checks again.  If the
 113    * version number changed, or if the semaphore was nonexistant at
 114    * either time, the reader sleeps for a second to let things
 115    * stabilize, and then tries again; if it does not succeed after
 116    * KRB5_DBM_MAX_RETRY attempts, it gives up.
 117    * 
 118    * On update, the semaphore file is deleted (if it exists) before any
 119    * update takes place; at the end of the update, it is replaced, with
 120    * a version number strictly greater than the version number which
 121    * existed at the start of the update.
 122    * 
 123    * If the system crashes in the middle of an update, the semaphore
 124    * file is not automatically created on reboot; this is a feature, not
 125    * a bug, since the database may be inconsistant.  Note that the
 126    * absence of a semaphore file does not prevent another _update_ from
 127    * taking place later.  Database replacements take place automatically
 128    * only on slave servers; a crash in the middle of an update will be
 129    * fixed by the next slave propagation.  A crash in the middle of an
 130    * update on the master would be somewhat more serious, but this would
 131    * likely be noticed by an administrator, who could fix the problem and
 132    * retry the operation.
 133    */
 134   
 135   #define free_dbsuffix(name) free(name)
 136   
 137   /*
 138    * Routines to deal with context.
 139    */
 140   #define        k5db2_inited(c)        (c && c->db_context &&        \
 141                            ((krb5_db2_context *) c->db_context)->db_inited)
 142   
 143   /*
 144    * Restore the default context.
 145    */
 146   static void
 147   k5db2_clear_context(dbctx)
 148       krb5_db2_context *dbctx;
 149   {
 150       /*
 151        * Free any dynamically allocated memory.  File descriptors and locks
 152        * are the caller's problem.
 153        */
 154       if (dbctx->db_lf_name)
 155           free(dbctx->db_lf_name);
 156       if (dbctx->db_name && (dbctx->db_name != default_db_name))
 157           free(dbctx->db_name);
 158       /*
 159        * Clear the structure and reset the defaults.
 160        */
 161       memset((char *) dbctx, 0, sizeof(krb5_db2_context));
 162       dbctx->db_name = default_db_name;
 163       dbctx->db_nb_locks = FALSE;
 164   }

 ----Unchanged portion omitted----

 968   
 969   /*
 970     Stores the *"nentries" entry structures pointed to by "entries" in the
 971     database.
 972   
 973     *"nentries" is updated upon return to reflect the number of records
 974     acutally stored; the first *"nstored" records will have been stored in the
 975     database (even if an error occurs).
 976   
 977    */
 978   
 979   krb5_error_code
 980   krb5_db2_db_put_principal(context, entries, nentries)
 981       krb5_context context;
 982       krb5_db_entry *entries;
 983       register int *nentries;                /* number of entry structs to update */
 984   {
 985       int i, n, dbret;
 986       DB *db;
 987       DBT key, contents;
 988       krb5_data contdata, keydata;
 989       krb5_error_code retval;
 990       krb5_db2_context *db_ctx;
 991 +     kdb_incr_update_t *upd, *fupd;
 992 +     char *princ_name = NULL;
 993 +     kdb_log_context *log_ctx;
 994   
 995 +     log_ctx = context->kdblog_context;
 996 + 
 997       n = *nentries;
 998       *nentries = 0;
 999       if (!k5db2_inited(context))
1000           return KRB5_KDB_DBNOTINITED;
1001   
1002       db_ctx = (krb5_db2_context *) context->db_context;
 997 |     if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE)))
1003 |     if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE))) {
1004           return retval;
1005 +     }
1006   
1007 +     /*
1008 +      * We need the lock since ulog_conv_2logentry() does a get
1009 +      */
1010 +     if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
1011 +         if (!(upd = (kdb_incr_update_t *)
1012 +           malloc(sizeof (kdb_incr_update_t)*n))) {
1013 +             retval = errno;
1014 +             goto err_lock;
1015 +         }
1016 +         fupd = upd;
1017 + 
1018 +         (void) memset(upd, 0, sizeof(kdb_incr_update_t)*n);
1019 + 
1020 +         if ((retval = ulog_conv_2logentry(context, entries, upd, n))) {
1021 +             goto err_lock;
1022 +         }
1023 +     }
1024 + 
1025       db = db_ctx->db;
1026       if ((retval = krb5_db2_db_start_update(context))) {
1002 |         (void)krb5_db2_db_unlock(context);
1027 |         goto err_lock;
1003 -         return retval;
1028       }
1029   
1030       /* for each one, stuff temps, and do replace/append */
1031       for (i = 0; i < n; i++) {
1032 +         /*
1033 +          * We'll be sharing the same locks as db for logging
1034 +          */
1035 +         if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
1036 +                 if ((retval = krb5_unparse_name(context, entries->princ,
1037 +                     &princ_name)))
1038 +                         goto err_lock;
1039 + 
1040 +                 upd->kdb_princ_name.utf8str_t_val = princ_name;
1041 +                 upd->kdb_princ_name.utf8str_t_len = strlen(princ_name);
1042 + 
1043 +                 if (retval = ulog_add_update(context, upd))
1044 +                         goto err_lock;
1045 +         }
1046 + 
1047           retval = krb5_encode_princ_contents(context, &contdata, entries);
1048           if (retval)
1049               break;
1050           contents.data = contdata.data;
1051           contents.size = contdata.length;
1052           retval = krb5_encode_princ_dbkey(context, &keydata, entries->princ);
1053           if (retval) {
1054               krb5_free_data_contents(context, &contdata);
1055               break;
1056           }
1057   
1058           key.data = keydata.data;
1059           key.size = keydata.length;
1060           dbret = (*db->put)(db, &key, &contents, 0);
1061           retval = dbret ? errno : 0;
1062           krb5_free_data_contents(context, &keydata);
1063           krb5_free_data_contents(context, &contdata);
1064           if (retval)
1065               break;
1066 +         else if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
1067 +             /*
1068 +              * We need to make sure the db record is synced before we mark
1069 +              * it as committed via finish_update.
1070 +              */
1071 +             dbret = (*db->sync)(db, 0);
1072 +             if (dbret) {
1073 +                 retval = errno;
1074 +                 goto err_lock;
1075 +             }
1076 +             (void) ulog_finish_update(context, upd);
1077 +             upd++;
1078 +         }
1079           entries++;                        /* bump to next struct */
1080       }
1081   
1082       (void)krb5_db2_db_end_update(context);
1083 + 
1084 + err_lock:
1085       (void)krb5_db2_db_unlock(context);                /* unlock database */
1086 + 
1087 +     if (log_ctx && (log_ctx->iproprole == IPROP_MASTER))
1088 +         ulog_free_entries(fupd, n);
1089 + 
1090       *nentries = i;
1091       return(retval);
1092   }
1093   
1094   /*
1095    * delete a principal from the data base.
1096    * returns number of entries removed
1097    */
1098   
1099   krb5_error_code
1100   krb5_db2_db_delete_principal(context, searchfor, nentries)
1101       krb5_context context;
1102       krb5_const_principal searchfor;
1103       int *nentries;                /* how many found & deleted */
1104   {
1105       krb5_error_code retval;
1106       krb5_db_entry entry;
1107       krb5_db2_context *db_ctx;
1108       DB *db;
1109       DBT key, contents;
1110       krb5_data keydata, contdata;
1111       int i, dbret;
1112 +     kdb_incr_update_t upd;
1113 +     char *princ_name = NULL;
1114 +     kdb_log_context *log_ctx;
1115   
1116 +     log_ctx = context->kdblog_context;
1117 + 
1118       if (!k5db2_inited(context))
1119           return KRB5_KDB_DBNOTINITED;
1120   
1121       db_ctx = (krb5_db2_context *) context->db_context;
1122       if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE)))
1123           return(retval);
1124   
1125       if ((retval = krb5_db2_db_start_update(context))) {
1126           (void) krb5_db2_db_unlock(context); /* unlock write lock */
1127           return(retval);
1128       }
1129   
1130       if ((retval = krb5_encode_princ_dbkey(context, &keydata, searchfor)))
1131           goto cleanup;
1132       key.data = keydata.data;
1133       key.size = keydata.length;
1134   
1135       db = db_ctx->db;
1136       dbret = (*db->get)(db, &key, &contents, 0);
1137       retval = errno;
1138       switch (dbret) {
1139       case 1:
1140           retval = KRB5_KDB_NOENTRY;
1141       case -1:
1142       default:
1143           *nentries = 0;
1144           goto cleankey;
1145       case 0:
1146           ;
1147       }
1148 + 
1149 +     /*
1150 +      * We'll be sharing the same locks as db for logging
1151 +      */
1152 +     if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
1153 +         if ((retval = krb5_unparse_name(context, searchfor, &princ_name))) {
1154 +                 (void) krb5_db2_db_unlock(context);
1155 +                 return retval;
1156 +         }
1157 + 
1158 +         (void) memset(&upd, 0, sizeof (kdb_incr_update_t));
1159 + 
1160 +         upd.kdb_princ_name.utf8str_t_val = princ_name;
1161 +         upd.kdb_princ_name.utf8str_t_len = strlen(princ_name);
1162 + 
1163 +         if (retval = ulog_delete_update(context, &upd)) {
1164 +                 free(princ_name);
1165 +                 (void) krb5_db2_db_unlock(context);
1166 +                 return retval;
1167 +         }
1168 + 
1169 +         free(princ_name);
1170 +     }
1171 + 
1172       memset((char *)&entry, 0, sizeof(entry));
1173       contdata.data = contents.data;
1174       contdata.length = contents.size;
1175       retval = krb5_decode_princ_contents(context, &contdata, &entry);
1176       if (retval)
1177           goto cleankey;
1178       *nentries = 1;
1179   
1180       /* Clear encrypted key contents */
1181       for (i = 0; i < entry.n_key_data; i++) {
1182           if (entry.key_data[i].key_data_length[0]) {
1183               memset((char *)entry.key_data[i].key_data_contents[0], 0, 
1184                      entry.key_data[i].key_data_length[0]); 
1185           }
1186       }
1187   
1188       retval = krb5_encode_princ_contents(context, &contdata, &entry);
1189       krb5_dbe_free_contents(context, &entry);
1190       if (retval)
1191           goto cleankey;
1192   
1193       contents.data = contdata.data;
1194       contents.size = contdata.length;
1195       dbret = (*db->put)(db, &key, &contents, 0);
1196       retval = dbret ? errno : 0;
1197       krb5_free_data_contents(context, &contdata);
1198       if (retval)
1199           goto cleankey;
1200       dbret = (*db->del)(db, &key, 0);
1201       retval = dbret ? errno : 0;
1202 + 
1203 +     /*
1204 +      * We need to commit our update upon success
1205 +      */
1206 +     if (!retval)
1207 +         if (log_ctx && (log_ctx->iproprole == IPROP_MASTER))
1208 +                 (void) ulog_finish_update(context, &upd);
1209 + 
1210   cleankey:
1211       krb5_free_data_contents(context, &keydata);
1212   
1213   cleanup:
1214       (void) krb5_db2_db_end_update(context);
1215       (void) krb5_db2_db_unlock(context);        /* unlock write lock */
1216       return retval;
1217   }

 ----Unchanged portion omitted----