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----