/* * Walk the keytab, looking for entries of this principal name, * with KVNO other than current kvno -1. * * These entries are now stale, * we only keep the current and previous entries around. * * Inspired by the code in Samba3 for 'use kerberos keytab'. */ krb5_error_code smb_krb5_remove_obsolete_keytab_entries(TALLOC_CTX *mem_ctx, krb5_context context, krb5_keytab keytab, uint32_t num_principals, krb5_principal *principals, krb5_kvno kvno, bool *found_previous, const char **error_string) { TALLOC_CTX *tmp_ctx; krb5_error_code code; krb5_kt_cursor cursor; tmp_ctx = talloc_new(mem_ctx); if (tmp_ctx == NULL) { *error_string = "Cannot allocate tmp_ctx"; return ENOMEM; } *found_previous = true; code = krb5_kt_start_seq_get(context, keytab, &cursor); switch (code) { case 0: break; #ifdef HEIM_ERR_OPNOTSUPP case HEIM_ERR_OPNOTSUPP: #endif case ENOENT: case KRB5_KT_END: /* no point enumerating if there isn't anything here */ code = 0; goto done; default: *error_string = talloc_asprintf(mem_ctx, "failed to open keytab for read of old entries: %s\n", smb_get_krb5_error_message(context, code, mem_ctx)); goto done; } do { krb5_kvno old_kvno = kvno - 1; krb5_keytab_entry entry; bool matched = false; uint32_t i; code = krb5_kt_next_entry(context, keytab, &entry, &cursor); if (code) { break; } for (i = 0; i < num_principals; i++) { krb5_boolean ok; ok = smb_krb5_kt_compare(context, &entry, principals[i], 0, 0); if (ok) { matched = true; break; } } if (!matched) { /* * Free the entry, it wasn't the one we were looking * for anyway */ krb5_kt_free_entry(context, &entry); /* Make sure we do not double free */ ZERO_STRUCT(entry); continue; } /* * Delete it, if it is not kvno - 1. * * Some keytab files store the kvno only in 8bits. Limit the * compare to 8bits, so that we don't miss old keys and delete * them. */ if ((entry.vno & 0xff) != (old_kvno & 0xff)) { krb5_error_code rc; /* Release the enumeration. We are going to * have to start this from the top again, * because deletes during enumeration may not * always be consistent. * * Also, the enumeration locks a FILE: keytab */ krb5_kt_end_seq_get(context, keytab, &cursor); code = krb5_kt_remove_entry(context, keytab, &entry); krb5_kt_free_entry(context, &entry); /* Make sure we do not double free */ ZERO_STRUCT(entry); /* Deleted: Restart from the top */ rc = krb5_kt_start_seq_get(context, keytab, &cursor); if (rc != 0) { krb5_kt_free_entry(context, &entry); /* Make sure we do not double free */ ZERO_STRUCT(entry); DEBUG(1, ("failed to restart enumeration of keytab: %s\n", smb_get_krb5_error_message(context, code, tmp_ctx))); talloc_free(tmp_ctx); return rc; } if (code != 0) { break; } } else { *found_previous = true; } /* Free the entry, we don't need it any more */ krb5_kt_free_entry(context, &entry); /* Make sure we do not double free */ ZERO_STRUCT(entry); } while (code != 0); krb5_kt_end_seq_get(context, keytab, &cursor); switch (code) { case 0: break; case ENOENT: case KRB5_KT_END: code = 0; break; default: *error_string = talloc_asprintf(mem_ctx, "failed in deleting old entries for principal: %s\n", smb_get_krb5_error_message(context, code, mem_ctx)); } code = 0; done: talloc_free(tmp_ctx); return code; }
static krb5_error_code remove_old_entries(TALLOC_CTX *parent_ctx, int kvno, krb5_principal *principals, bool delete_all_kvno, krb5_context context, krb5_keytab keytab, bool *found_previous, const char **error_string) { krb5_error_code ret, ret2; krb5_kt_cursor cursor; TALLOC_CTX *mem_ctx = talloc_new(parent_ctx); if (!mem_ctx) { return ENOMEM; } *found_previous = false; /* for each entry in the keytab */ ret = krb5_kt_start_seq_get(context, keytab, &cursor); switch (ret) { case 0: break; #ifdef HEIM_ERR_OPNOTSUPP case HEIM_ERR_OPNOTSUPP: #endif case ENOENT: case KRB5_KT_END: /* no point enumerating if there isn't anything here */ talloc_free(mem_ctx); return 0; default: *error_string = talloc_asprintf(parent_ctx, "failed to open keytab for read of old entries: %s\n", smb_get_krb5_error_message(context, ret, mem_ctx)); talloc_free(mem_ctx); return ret; } while (!ret) { unsigned int i; bool matched = false; krb5_keytab_entry entry; ret = krb5_kt_next_entry(context, keytab, &entry, &cursor); if (ret) { break; } for (i = 0; principals[i]; i++) { /* if it matches our principal */ if (smb_krb5_kt_compare(context, &entry, principals[i], 0, 0)) { matched = true; break; } } if (!matched) { /* Free the entry, * it wasn't the one we were looking for anyway */ krb5_kt_free_entry(context, &entry); continue; } /* delete it, if it is not kvno -1 */ if (entry.vno != (kvno - 1 )) { /* Release the enumeration. We are going to * have to start this from the top again, * because deletes during enumeration may not * always be consistent. * * Also, the enumeration locks a FILE: keytab */ krb5_kt_end_seq_get(context, keytab, &cursor); ret = krb5_kt_remove_entry(context, keytab, &entry); krb5_kt_free_entry(context, &entry); /* Deleted: Restart from the top */ ret2 = krb5_kt_start_seq_get(context, keytab, &cursor); if (ret2) { krb5_kt_free_entry(context, &entry); DEBUG(1, ("failed to restart enumeration of keytab: %s\n", smb_get_krb5_error_message(context, ret, mem_ctx))); talloc_free(mem_ctx); return ret2; } if (ret) { break; } } else { *found_previous = true; } /* Free the entry, we don't need it any more */ krb5_kt_free_entry(context, &entry); } krb5_kt_end_seq_get(context, keytab, &cursor); switch (ret) { case 0: break; case ENOENT: case KRB5_KT_END: ret = 0; break; default: *error_string = talloc_asprintf(parent_ctx, "failed in deleting old entries for principal: %s\n", smb_get_krb5_error_message(context, ret, mem_ctx)); } talloc_free(mem_ctx); return ret; }