1/* 2 Unix SMB/CIFS implementation. 3 dump the remote SAM using rpc samsync operations 4 5 Copyright (C) Guenther Deschner 2008. 6 Copyright (C) Michael Adam 2008 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 3 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program. If not, see <http://www.gnu.org/licenses/>. 20*/ 21 22#include "includes.h" 23#include "libnet/libnet.h" 24 25#ifdef HAVE_KRB5 26 27/**************************************************************** 28****************************************************************/ 29 30static int keytab_close(struct libnet_keytab_context *ctx) 31{ 32 if (!ctx) { 33 return 0; 34 } 35 36 if (ctx->keytab && ctx->context) { 37 krb5_kt_close(ctx->context, ctx->keytab); 38 } 39 40 if (ctx->context) { 41 krb5_free_context(ctx->context); 42 } 43 44 if (ctx->ads) { 45 ads_destroy(&ctx->ads); 46 } 47 48 TALLOC_FREE(ctx); 49 50 return 0; 51} 52 53/**************************************************************** 54****************************************************************/ 55 56krb5_error_code libnet_keytab_init(TALLOC_CTX *mem_ctx, 57 const char *keytab_name, 58 struct libnet_keytab_context **ctx) 59{ 60 krb5_error_code ret = 0; 61 krb5_context context = NULL; 62 krb5_keytab keytab = NULL; 63 const char *keytab_string = NULL; 64 65 struct libnet_keytab_context *r; 66 67 r = TALLOC_ZERO_P(mem_ctx, struct libnet_keytab_context); 68 if (!r) { 69 return ENOMEM; 70 } 71 72 talloc_set_destructor(r, keytab_close); 73 74 initialize_krb5_error_table(); 75 ret = krb5_init_context(&context); 76 if (ret) { 77 DEBUG(1,("keytab_init: could not krb5_init_context: %s\n", 78 error_message(ret))); 79 return ret; 80 } 81 82 ret = smb_krb5_open_keytab(context, keytab_name, true, &keytab); 83 if (ret) { 84 DEBUG(1,("keytab_init: smb_krb5_open_keytab failed (%s)\n", 85 error_message(ret))); 86 krb5_free_context(context); 87 return ret; 88 } 89 90 ret = smb_krb5_keytab_name(mem_ctx, context, keytab, &keytab_string); 91 if (ret) { 92 krb5_kt_close(context, keytab); 93 krb5_free_context(context); 94 return ret; 95 } 96 97 r->context = context; 98 r->keytab = keytab; 99 r->keytab_name = keytab_string; 100 r->clean_old_entries = false; 101 102 *ctx = r; 103 104 return 0; 105} 106 107/**************************************************************** 108****************************************************************/ 109 110/** 111 * Remove all entries that have the given principal, kvno and enctype. 112 */ 113static krb5_error_code libnet_keytab_remove_entries(krb5_context context, 114 krb5_keytab keytab, 115 const char *principal, 116 int kvno, 117 const krb5_enctype enctype, 118 bool ignore_kvno) 119{ 120 krb5_error_code ret; 121 krb5_kt_cursor cursor; 122 krb5_keytab_entry kt_entry; 123 124 ZERO_STRUCT(kt_entry); 125 ZERO_STRUCT(cursor); 126 127 ret = krb5_kt_start_seq_get(context, keytab, &cursor); 128 if (ret) { 129 return 0; 130 } 131 132 while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) 133 { 134 krb5_keyblock *keyp; 135 char *princ_s = NULL; 136 137 if (kt_entry.vno != kvno && !ignore_kvno) { 138 goto cont; 139 } 140 141 keyp = KRB5_KT_KEY(&kt_entry); 142 143 if (KRB5_KEY_TYPE(keyp) != enctype) { 144 goto cont; 145 } 146 147 ret = smb_krb5_unparse_name(talloc_tos(), context, kt_entry.principal, 148 &princ_s); 149 if (ret) { 150 DEBUG(5, ("smb_krb5_unparse_name failed (%s)\n", 151 error_message(ret))); 152 goto cont; 153 } 154 155 if (strcmp(principal, princ_s) != 0) { 156 goto cont; 157 } 158 159 /* match found - remove */ 160 161 DEBUG(10, ("found entry for principal %s, kvno %d, " 162 "enctype %d - trying to remove it\n", 163 princ_s, kt_entry.vno, KRB5_KEY_TYPE(keyp))); 164 165 ret = krb5_kt_end_seq_get(context, keytab, &cursor); 166 ZERO_STRUCT(cursor); 167 if (ret) { 168 DEBUG(5, ("krb5_kt_end_seq_get failed (%s)\n", 169 error_message(ret))); 170 goto cont; 171 } 172 173 ret = krb5_kt_remove_entry(context, keytab, 174 &kt_entry); 175 if (ret) { 176 DEBUG(5, ("krb5_kt_remove_entry failed (%s)\n", 177 error_message(ret))); 178 goto cont; 179 } 180 DEBUG(10, ("removed entry for principal %s, kvno %d, " 181 "enctype %d\n", princ_s, kt_entry.vno, 182 KRB5_KEY_TYPE(keyp))); 183 184 ret = krb5_kt_start_seq_get(context, keytab, &cursor); 185 if (ret) { 186 DEBUG(5, ("krb5_kt_start_seq_get failed (%s)\n", 187 error_message(ret))); 188 goto cont; 189 } 190 191cont: 192 smb_krb5_kt_free_entry(context, &kt_entry); 193 TALLOC_FREE(princ_s); 194 } 195 196 ret = krb5_kt_end_seq_get(context, keytab, &cursor); 197 if (ret) { 198 DEBUG(5, ("krb5_kt_end_seq_get failed (%s)\n", 199 error_message(ret))); 200 } 201 202 return ret; 203} 204 205static krb5_error_code libnet_keytab_add_entry(krb5_context context, 206 krb5_keytab keytab, 207 krb5_kvno kvno, 208 const char *princ_s, 209 krb5_enctype enctype, 210 krb5_data password) 211{ 212 krb5_keyblock *keyp; 213 krb5_keytab_entry kt_entry; 214 krb5_error_code ret; 215 216 /* remove duplicates first ... */ 217 ret = libnet_keytab_remove_entries(context, keytab, princ_s, kvno, 218 enctype, false); 219 if (ret) { 220 DEBUG(1, ("libnet_keytab_remove_entries failed: %s\n", 221 error_message(ret))); 222 } 223 224 ZERO_STRUCT(kt_entry); 225 226 kt_entry.vno = kvno; 227 228 ret = smb_krb5_parse_name(context, princ_s, &kt_entry.principal); 229 if (ret) { 230 DEBUG(1, ("smb_krb5_parse_name(%s) failed (%s)\n", 231 princ_s, error_message(ret))); 232 return ret; 233 } 234 235 keyp = KRB5_KT_KEY(&kt_entry); 236 237 if (create_kerberos_key_from_string(context, kt_entry.principal, 238 &password, keyp, enctype, true)) 239 { 240 ret = KRB5KRB_ERR_GENERIC; 241 goto done; 242 } 243 244 ret = krb5_kt_add_entry(context, keytab, &kt_entry); 245 if (ret) { 246 DEBUG(1, ("adding entry to keytab failed (%s)\n", 247 error_message(ret))); 248 } 249 250done: 251 krb5_free_keyblock_contents(context, keyp); 252 krb5_free_principal(context, kt_entry.principal); 253 ZERO_STRUCT(kt_entry); 254 smb_krb5_kt_free_entry(context, &kt_entry); 255 256 return ret; 257} 258 259krb5_error_code libnet_keytab_add(struct libnet_keytab_context *ctx) 260{ 261 krb5_error_code ret = 0; 262 uint32_t i; 263 264 265 if (ctx->clean_old_entries) { 266 DEBUG(0, ("cleaning old entries...\n")); 267 for (i=0; i < ctx->count; i++) { 268 struct libnet_keytab_entry *entry = &ctx->entries[i]; 269 270 ret = libnet_keytab_remove_entries(ctx->context, 271 ctx->keytab, 272 entry->principal, 273 0, 274 entry->enctype, 275 true); 276 if (ret) { 277 DEBUG(1,("libnet_keytab_add: Failed to remove " 278 "old entries for %s (enctype %u): %s\n", 279 entry->principal, entry->enctype, 280 error_message(ret))); 281 return ret; 282 } 283 } 284 } 285 286 for (i=0; i<ctx->count; i++) { 287 288 struct libnet_keytab_entry *entry = &ctx->entries[i]; 289 krb5_data password; 290 291 ZERO_STRUCT(password); 292 password.data = (char *)entry->password.data; 293 password.length = entry->password.length; 294 295 ret = libnet_keytab_add_entry(ctx->context, 296 ctx->keytab, 297 entry->kvno, 298 entry->principal, 299 entry->enctype, 300 password); 301 if (ret) { 302 DEBUG(1,("libnet_keytab_add: " 303 "Failed to add entry to keytab file\n")); 304 return ret; 305 } 306 } 307 308 return ret; 309} 310 311struct libnet_keytab_entry *libnet_keytab_search(struct libnet_keytab_context *ctx, 312 const char *principal, 313 int kvno, 314 const krb5_enctype enctype, 315 TALLOC_CTX *mem_ctx) 316{ 317 krb5_error_code ret = 0; 318 krb5_kt_cursor cursor; 319 krb5_keytab_entry kt_entry; 320 struct libnet_keytab_entry *entry = NULL; 321 322 ZERO_STRUCT(kt_entry); 323 ZERO_STRUCT(cursor); 324 325 ret = krb5_kt_start_seq_get(ctx->context, ctx->keytab, &cursor); 326 if (ret) { 327 DEBUG(10, ("krb5_kt_start_seq_get failed: %s\n", 328 error_message(ret))); 329 return NULL; 330 } 331 332 while (krb5_kt_next_entry(ctx->context, ctx->keytab, &kt_entry, &cursor) == 0) 333 { 334 krb5_keyblock *keyp; 335 char *princ_s = NULL; 336 337 entry = NULL; 338 339 if (kt_entry.vno != kvno) { 340 goto cont; 341 } 342 343 keyp = KRB5_KT_KEY(&kt_entry); 344 345 if (KRB5_KEY_TYPE(keyp) != enctype) { 346 goto cont; 347 } 348 349 entry = talloc_zero(mem_ctx, struct libnet_keytab_entry); 350 if (!entry) { 351 DEBUG(3, ("talloc failed\n")); 352 goto fail; 353 } 354 355 ret = smb_krb5_unparse_name(entry, ctx->context, kt_entry.principal, 356 &princ_s); 357 if (ret) { 358 goto cont; 359 } 360 361 if (strcmp(principal, princ_s) != 0) { 362 goto cont; 363 } 364 365 entry->principal = talloc_strdup(entry, princ_s); 366 if (!entry->principal) { 367 DEBUG(3, ("talloc_strdup_failed\n")); 368 goto fail; 369 } 370 371 entry->name = talloc_move(entry, &princ_s); 372 373 entry->password = data_blob_talloc(entry, KRB5_KEY_DATA(keyp), 374 KRB5_KEY_LENGTH(keyp)); 375 if (!entry->password.data) { 376 DEBUG(3, ("data_blob_talloc failed\n")); 377 goto fail; 378 } 379 380 DEBUG(10, ("found entry\n")); 381 382 smb_krb5_kt_free_entry(ctx->context, &kt_entry); 383 break; 384 385fail: 386 smb_krb5_kt_free_entry(ctx->context, &kt_entry); 387 TALLOC_FREE(entry); 388 break; 389 390cont: 391 smb_krb5_kt_free_entry(ctx->context, &kt_entry); 392 TALLOC_FREE(entry); 393 continue; 394 } 395 396 krb5_kt_end_seq_get(ctx->context, ctx->keytab, &cursor); 397 return entry; 398} 399 400/** 401 * Helper function to add data to the list 402 * of keytab entries. It builds the prefix from the input. 403 */ 404NTSTATUS libnet_keytab_add_to_keytab_entries(TALLOC_CTX *mem_ctx, 405 struct libnet_keytab_context *ctx, 406 uint32_t kvno, 407 const char *name, 408 const char *prefix, 409 const krb5_enctype enctype, 410 DATA_BLOB blob) 411{ 412 struct libnet_keytab_entry entry; 413 414 entry.kvno = kvno; 415 entry.name = talloc_strdup(mem_ctx, name); 416 entry.principal = talloc_asprintf(mem_ctx, "%s%s%s@%s", 417 prefix ? prefix : "", 418 prefix ? "/" : "", 419 name, ctx->dns_domain_name); 420 entry.enctype = enctype; 421 entry.password = blob; 422 NT_STATUS_HAVE_NO_MEMORY(entry.name); 423 NT_STATUS_HAVE_NO_MEMORY(entry.principal); 424 NT_STATUS_HAVE_NO_MEMORY(entry.password.data); 425 426 ADD_TO_ARRAY(mem_ctx, struct libnet_keytab_entry, entry, 427 &ctx->entries, &ctx->count); 428 NT_STATUS_HAVE_NO_MEMORY(ctx->entries); 429 430 return NT_STATUS_OK; 431} 432 433#endif /* HAVE_KRB5 */ 434