/* add or modify a rdataset */ _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, void *dbdata, void *version) { struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data); struct dnsp_DnssrvRpcRecord *rec; struct ldb_dn *dn; isc_result_t result; struct ldb_result *res; const char *attrs[] = { "dnsRecord", NULL }; int ret, i; struct ldb_message_element *el; enum ndr_err_code ndr_err; NTTIME t; if (state->transaction_token != (void*)version) { state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version"); return ISC_R_FAILURE; } rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord); if (rec == NULL) { return ISC_R_NOMEMORY; } unix_to_nt_time(&t, time(NULL)); t /= 10*1000*1000; /* convert to seconds (NT time is in 100ns units) */ t /= 3600; /* convert to hours */ rec->rank = DNS_RANK_ZONE; rec->dwSerial = state->soa_serial; rec->dwTimeStamp = (uint32_t)t; if (!b9_parse(state, rdatastr, rec)) { state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr); talloc_free(rec); return ISC_R_FAILURE; } /* find the DN of the record */ result = b9_find_name_dn(state, name, rec, &dn); if (result != ISC_R_SUCCESS) { talloc_free(rec); return result; } /* get any existing records */ ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode"); if (ret == LDB_ERR_NO_SUCH_OBJECT) { result = b9_add_record(state, name, dn, rec); talloc_free(rec); if (result == ISC_R_SUCCESS) { state->log(ISC_LOG_ERROR, "samba_dlz: added %s %s", name, rdatastr); } return result; } /* there are existing records. We need to see if this will * replace a record or add to it */ el = ldb_msg_find_element(res->msgs[0], "dnsRecord"); if (el == NULL) { state->log(ISC_LOG_ERROR, "samba_dlz: no dnsRecord attribute for %s", ldb_dn_get_linearized(dn)); talloc_free(rec); return ISC_R_FAILURE; } for (i=0; i<el->num_values; i++) { struct dnsp_DnssrvRpcRecord rec2; ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2, (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s", ldb_dn_get_linearized(dn)); talloc_free(rec); return ISC_R_FAILURE; } if (b9_record_match(state, rec, &rec2)) { break; } } if (i == el->num_values) { /* adding a new value */ el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values+1); if (el->values == NULL) { talloc_free(rec); return ISC_R_NOMEMORY; } el->num_values++; }
/* authorize a zone update */ _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr, const char *type, const char *key, uint32_t keydatalen, uint8_t *keydata, void *dbdata) { struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data); TALLOC_CTX *tmp_ctx; DATA_BLOB ap_req; struct cli_credentials *server_credentials; char *keytab_name; int ret; int ldb_ret; NTSTATUS nt_status; struct gensec_security *gensec_ctx; struct auth_session_info *session_info; struct ldb_dn *dn; isc_result_t result; struct ldb_result *res; const char * attrs[] = { NULL }; uint32_t access_mask; /* Remove cached credentials, if any */ if (state->session_info) { talloc_free(state->session_info); state->session_info = NULL; } if (state->update_name) { talloc_free(state->update_name); state->update_name = NULL; } tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { state->log(ISC_LOG_ERROR, "samba_dlz: no memory"); return ISC_FALSE; } ap_req = data_blob_const(keydata, keydatalen); server_credentials = cli_credentials_init(tmp_ctx); if (!server_credentials) { state->log(ISC_LOG_ERROR, "samba_dlz: failed to init server credentials"); talloc_free(tmp_ctx); return ISC_FALSE; } cli_credentials_set_krb5_context(server_credentials, state->smb_krb5_ctx); cli_credentials_set_conf(server_credentials, state->lp); keytab_name = talloc_asprintf(tmp_ctx, "file:%s/dns.keytab", lpcfg_private_dir(state->lp)); ret = cli_credentials_set_keytab_name(server_credentials, state->lp, keytab_name, CRED_SPECIFIED); if (ret != 0) { state->log(ISC_LOG_ERROR, "samba_dlz: failed to obtain server credentials from %s", keytab_name); talloc_free(tmp_ctx); return ISC_FALSE; } talloc_free(keytab_name); nt_status = gensec_server_start(tmp_ctx, lpcfg_gensec_settings(tmp_ctx, state->lp), state->auth_context, &gensec_ctx); if (!NT_STATUS_IS_OK(nt_status)) { state->log(ISC_LOG_ERROR, "samba_dlz: failed to start gensec server"); talloc_free(tmp_ctx); return ISC_FALSE; } gensec_set_credentials(gensec_ctx, server_credentials); nt_status = gensec_start_mech_by_name(gensec_ctx, "spnego"); if (!NT_STATUS_IS_OK(nt_status)) { state->log(ISC_LOG_ERROR, "samba_dlz: failed to start spnego"); talloc_free(tmp_ctx); return ISC_FALSE; } nt_status = gensec_update(gensec_ctx, tmp_ctx, state->ev_ctx, ap_req, &ap_req); if (!NT_STATUS_IS_OK(nt_status)) { state->log(ISC_LOG_ERROR, "samba_dlz: spnego update failed"); talloc_free(tmp_ctx); return ISC_FALSE; } nt_status = gensec_session_info(gensec_ctx, tmp_ctx, &session_info); if (!NT_STATUS_IS_OK(nt_status)) { state->log(ISC_LOG_ERROR, "samba_dlz: failed to create session info"); talloc_free(tmp_ctx); return ISC_FALSE; } /* Get the DN from name */ result = b9_find_name_dn(state, name, tmp_ctx, &dn); if (result != ISC_R_SUCCESS) { state->log(ISC_LOG_ERROR, "samba_dlz: failed to find name %s", name); talloc_free(tmp_ctx); return ISC_FALSE; } /* make sure the dn exists, or find parent dn in case new object is being added */ ldb_ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode"); if (ldb_ret == LDB_ERR_NO_SUCH_OBJECT) { ldb_dn_remove_child_components(dn, 1); access_mask = SEC_ADS_CREATE_CHILD; talloc_free(res); } else if (ldb_ret == LDB_SUCCESS) { access_mask = SEC_STD_REQUIRED | SEC_ADS_SELF_WRITE; talloc_free(res); } else { talloc_free(tmp_ctx); return ISC_FALSE; } /* Do ACL check */ ldb_ret = dsdb_check_access_on_dn(state->samdb, tmp_ctx, dn, session_info->security_token, access_mask, NULL); if (ldb_ret != LDB_SUCCESS) { state->log(ISC_LOG_INFO, "samba_dlz: disallowing update of signer=%s name=%s type=%s error=%s", signer, name, type, ldb_strerror(ldb_ret)); talloc_free(tmp_ctx); return ISC_FALSE; } /* Cache session_info, so it can be used in the actual add/delete operation */ state->update_name = talloc_strdup(state, name); if (state->update_name == NULL) { state->log(ISC_LOG_ERROR, "samba_dlz: memory allocation error"); talloc_free(tmp_ctx); return ISC_FALSE; } state->session_info = talloc_steal(state, session_info); state->log(ISC_LOG_INFO, "samba_dlz: allowing update of signer=%s name=%s tcpaddr=%s type=%s key=%s", signer, name, tcpaddr, type, key); talloc_free(tmp_ctx); return ISC_TRUE; }