BOOL prs_werror(const char *name, prs_struct *ps, int depth, WERROR *status) { char *q = prs_mem_get(ps, sizeof(uint32)); if (q == NULL) return False; if (UNMARSHALLING(ps)) { if (ps->bigendian_data) *status = W_ERROR(RIVAL(q,0)); else *status = W_ERROR(IVAL(q,0)); } else { if (ps->bigendian_data) RSIVAL(q,0,W_ERROR_V(*status)); else SIVAL(q,0,W_ERROR_V(*status)); } DEBUG(5,("%s%04x %s: %s\n", tab_depth(depth), ps->data_offset, name, dos_errstr(*status))); ps->data_offset += sizeof(uint32); return True; }
int buffer_next_uint32(xrtp_buffer_t * buf, uint32 * ret){ if(buf->pos + sizeof(uint32) > buf->len){ return 0; } if(HOST_ORDER == BIGEND_ORDER) { buffer_log(("buffer_next_uint32: BE (%u) at buf[%d]@%d\n", *ret, buf->pos, (int)(buf->data))); *ret = RIVAL(buf->data, buf->pos); } else { buffer_log(("buffer_next_uint32: LE (%u) at buf[%d]@%d\n", *ret, buf->pos, (int)(buf->data))); *ret = IVAL(buf->data, buf->pos); } if(HOST_ORDER != buf->byte_order) { buffer_log(("buffer_next_uint32: HE (%u) at buf[%d]@%d\n", *ret, buf->pos, (int)(buf->data))); _buffer_swap32(*ret); } buf->pos += sizeof(uint32); return sizeof(uint32); }
BOOL prs_ntstatus(const char *name, prs_struct *ps, int depth, NTSTATUS *status) { char *q = prs_mem_get(ps, sizeof(uint32)); if (q == NULL) return False; if (UNMARSHALLING(ps)) { if (ps->bigendian_data) *status = NT_STATUS(RIVAL(q,0)); else *status = NT_STATUS(IVAL(q,0)); } else { if (ps->bigendian_data) RSIVAL(q,0,NT_STATUS_V(*status)); else SIVAL(q,0,NT_STATUS_V(*status)); } DEBUG(5,("%s%04x %s: %s\n", tab_depth(depth), ps->data_offset, name, get_nt_error_msg(*status))); ps->data_offset += sizeof(uint32); return True; }
bool prs_dcerpc_status(const char *name, prs_struct *ps, int depth, NTSTATUS *status) { char *q = prs_mem_get(ps, sizeof(uint32)); if (q == NULL) return False; if (UNMARSHALLING(ps)) { if (ps->bigendian_data) *status = NT_STATUS(RIVAL(q,0)); else *status = NT_STATUS(IVAL(q,0)); } else { if (ps->bigendian_data) RSIVAL(q,0,NT_STATUS_V(*status)); else SIVAL(q,0,NT_STATUS_V(*status)); } DEBUGADD(5,("%s%04x %s: %s\n", tab_depth(5,depth), ps->data_offset, name, dcerpc_errstr(talloc_tos(), NT_STATUS_V(*status)))); ps->data_offset += sizeof(uint32); return True; }
/* work out if a packet is complete for protocols that use a 32 bit network byte order length */ _PUBLIC_ NTSTATUS packet_full_request_u32(void *private_data, DATA_BLOB blob, size_t *size) { if (blob.length < 4) { return STATUS_MORE_ENTRIES; } *size = 4 + RIVAL(blob.data, 0); if (*size > blob.length) { return STATUS_MORE_ENTRIES; } return NT_STATUS_OK; }
static NTSTATUS smbXsrv_tcon_local_key_to_id(TDB_DATA key, uint32_t *id) { if (id == NULL) { return NT_STATUS_INVALID_PARAMETER; } if (key.dsize != SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE) { return NT_STATUS_INTERNAL_DB_CORRUPTION; } *id = RIVAL(key.dptr, 0); return NT_STATUS_OK; }
/* this function is due to be replaced */ void initrpcreply(char *inbuf, char *q) { uint32 callid; SCVAL(q, 0, 5); q++; /* RPC version 5 */ SCVAL(q, 0, 0); q++; /* minor version 0 */ SCVAL(q, 0, 2); q++; /* RPC response packet */ SCVAL(q, 0, 3); q++; /* first frag + last frag */ RSIVAL(q, 0, 0x10000000); q += 4; /* packed data representation */ RSSVAL(q, 0, 0); q += 2; /* fragment length, fill in later */ SSVAL(q, 0, 0); q += 2; /* authentication length */ callid = RIVAL(inbuf, 12); RSIVAL(q, 0, callid); q += 4; /* call identifier - match incoming RPC */ SIVAL(q, 0, 0x18); q += 4; /* allocation hint (no idea) */ SSVAL(q, 0, 0); q += 2; /* presentation context identifier */ SCVAL(q, 0, 0); q++; /* cancel count */ SCVAL(q, 0, 0); q++; /* reserved */ }
/* * These functions are for use in the deprecated * gensec_socket code (public because SPNEGO must * use them for recursion) */ NTSTATUS gensec_unwrap_packets(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, const DATA_BLOB *in, DATA_BLOB *out, size_t *len_processed) { if (!gensec_security->ops->unwrap_packets) { DATA_BLOB wrapped; NTSTATUS nt_status; size_t packet_size; if (in->length < 4) { /* Missing the header we already had! */ DEBUG(0, ("Asked to unwrap packet of bogus length! How did we get the short packet?!\n")); return NT_STATUS_INVALID_PARAMETER; } packet_size = RIVAL(in->data, 0); wrapped = data_blob_const(in->data + 4, packet_size); if (wrapped.length > (in->length - 4)) { DEBUG(0, ("Asked to unwrap packed of bogus length %d > %d! How did we get this?!\n", (int)wrapped.length, (int)(in->length - 4))); return NT_STATUS_INTERNAL_ERROR; } nt_status = gensec_unwrap(gensec_security, mem_ctx, &wrapped, out); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } *len_processed = packet_size + 4; return nt_status; } return gensec_security->ops->unwrap_packets(gensec_security, mem_ctx, in, out, len_processed); }
BOOL prs_uint32s(BOOL charmode, const char *name, prs_struct *ps, int depth, uint32 *data32s, int len) { int i; char *q = prs_mem_get(ps, len * sizeof(uint32)); if (q == NULL) return False; if (UNMARSHALLING(ps)) { if (ps->bigendian_data) { for (i = 0; i < len; i++) data32s[i] = RIVAL(q, 4*i); } else { for (i = 0; i < len; i++) data32s[i] = IVAL(q, 4*i); } } else { if (ps->bigendian_data) { for (i = 0; i < len; i++) RSIVAL(q, 4*i, data32s[i]); } else { for (i = 0; i < len; i++) SIVAL(q, 4*i, data32s[i]); } } DEBUG(5,("%s%04x %s: ", tab_depth(depth), ps->data_offset, name)); if (charmode) print_asc(5, (unsigned char*)data32s, 4*len); else { for (i = 0; i < len; i++) DEBUG(5,("%08x ", data32s[i])); } DEBUG(5,("\n")); ps->data_offset += (len * sizeof(uint32)); return True; }
/* * These functions are for use in the deprecated * gensec_socket code (public because SPNEGO must * use them for recursion) */ NTSTATUS gensec_packet_full_request(struct gensec_security *gensec_security, DATA_BLOB blob, size_t *size) { if (gensec_security->ops->packet_full_request) { return gensec_security->ops->packet_full_request(gensec_security, blob, size); } if (gensec_security->ops->unwrap_packets) { if (blob.length) { *size = blob.length; return NT_STATUS_OK; } return STATUS_MORE_ENTRIES; } if (blob.length < 4) { return STATUS_MORE_ENTRIES; } *size = 4 + RIVAL(blob.data, 0); if (*size > blob.length) { return STATUS_MORE_ENTRIES; } return NT_STATUS_OK; }
BOOL prs_uint32(const char *name, prs_struct *ps, int depth, uint32 *data32) { char *q = prs_mem_get(ps, sizeof(uint32)); if (q == NULL) return False; if (UNMARSHALLING(ps)) { if (ps->bigendian_data) *data32 = RIVAL(q,0); else *data32 = IVAL(q,0); } else { if (ps->bigendian_data) RSIVAL(q,0,*data32); else SIVAL(q,0,*data32); } DEBUG(5,("%s%04x %s: %08x\n", tab_depth(depth), ps->data_offset, name, *data32)); ps->data_offset += sizeof(uint32); return True; }
/* this performs a SASL/gssapi bind we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl is very dependent on correctly configured DNS whereas this routine is much less fragile see RFC2078 and RFC2222 for details */ static ADS_STATUS ads_sasl_gssapi_do_bind(ADS_STRUCT *ads, const gss_name_t serv_name) { uint32_t minor_status; gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL; gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT; gss_OID mech_type = GSS_C_NULL_OID; gss_buffer_desc output_token, input_token; uint32_t req_flags, ret_flags; int conf_state; struct berval cred; struct berval *scred = NULL; int i=0; int gss_rc, rc; uint8_t *p; uint32_t max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED; uint8_t wrap_type = ADS_SASLWRAP_TYPE_PLAIN; ADS_STATUS status; struct ads_saslwrap *wrap = &ads->ldap_wrap_data; input_token.value = NULL; input_token.length = 0; status = ads_init_gssapi_cred(ads, &gss_cred); if (!ADS_ERR_OK(status)) { goto failed; } /* * Note: here we always ask the gssapi for sign and seal * as this is negotiated later after the mutal * authentication */ req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG; for (i=0; i < MAX_GSS_PASSES; i++) { gss_rc = gss_init_sec_context(&minor_status, gss_cred, &context_handle, serv_name, mech_type, req_flags, 0, NULL, &input_token, NULL, &output_token, &ret_flags, NULL); if (scred) { ber_bvfree(scred); scred = NULL; } if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) { status = ADS_ERROR_GSS(gss_rc, minor_status); goto failed; } cred.bv_val = (char *)output_token.value; cred.bv_len = output_token.length; rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL, &scred); if (rc != LDAP_SASL_BIND_IN_PROGRESS) { status = ADS_ERROR(rc); goto failed; } if (output_token.value) { gss_release_buffer(&minor_status, &output_token); } if (scred) { input_token.value = scred->bv_val; input_token.length = scred->bv_len; } else { input_token.value = NULL; input_token.length = 0; } if (gss_rc == 0) break; } gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token, &conf_state,NULL); if (scred) { ber_bvfree(scred); scred = NULL; } if (gss_rc) { status = ADS_ERROR_GSS(gss_rc, minor_status); goto failed; } p = (uint8_t *)output_token.value; #if 0 file_save("sasl_gssapi.dat", output_token.value, output_token.length); #endif if (p) { wrap_type = CVAL(p,0); SCVAL(p,0,0); max_msg_size = RIVAL(p,0); } gss_release_buffer(&minor_status, &output_token); if (!(wrap_type & wrap->wrap_type)) { /* * the server doesn't supports the wrap * type we want :-( */ DEBUG(0,("The ldap sasl wrap type doesn't match wanted[%d] server[%d]\n", wrap->wrap_type, wrap_type)); DEBUGADD(0,("You may want to set the 'client ldap sasl wrapping' option\n")); status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED); goto failed; } /* 0x58 is the minimum windows accepts */ if (max_msg_size < 0x58) { max_msg_size = 0x58; } output_token.length = 4; output_token.value = SMB_MALLOC(output_token.length); if (!output_token.value) { output_token.length = 0; status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); goto failed; } p = (uint8_t *)output_token.value; RSIVAL(p,0,max_msg_size); SCVAL(p,0,wrap->wrap_type); /* * we used to add sprintf("dn:%s", ads->config.bind_path) here. * but using ads->config.bind_path is the wrong! It should be * the DN of the user object! * * w2k3 gives an error when we send an incorrect DN, but sending nothing * is ok and matches the information flow used in GSS-SPNEGO. */ gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT, &output_token, /* used as *input* here. */ &conf_state, &input_token); /* Used as *output* here. */ if (gss_rc) { status = ADS_ERROR_GSS(gss_rc, minor_status); output_token.length = 0; SAFE_FREE(output_token.value); goto failed; } /* We've finished with output_token. */ SAFE_FREE(output_token.value); output_token.length = 0; cred.bv_val = (char *)input_token.value; cred.bv_len = input_token.length; rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL, &scred); gss_release_buffer(&minor_status, &input_token); status = ADS_ERROR(rc); if (!ADS_ERR_OK(status)) { goto failed; } if (wrap->wrap_type > ADS_SASLWRAP_TYPE_PLAIN) { gss_rc = gss_wrap_size_limit(&minor_status, context_handle, (wrap->wrap_type == ADS_SASLWRAP_TYPE_SEAL), GSS_C_QOP_DEFAULT, max_msg_size, &wrap->out.max_unwrapped); if (gss_rc) { status = ADS_ERROR_GSS(gss_rc, minor_status); goto failed; } wrap->out.sig_size = max_msg_size - wrap->out.max_unwrapped; wrap->in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */ wrap->in.max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED; status = ads_setup_sasl_wrapping(wrap->wrap_private_data, ads->ldap.ld, &ads_sasl_gssapi_ops, context_handle); if (!ADS_ERR_OK(status)) { DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n", ads_errstr(status))); goto failed; } /* make sure we don't free context_handle */ context_handle = GSS_C_NO_CONTEXT; } failed: if (gss_cred != GSS_C_NO_CREDENTIAL) gss_release_cred(&minor_status, &gss_cred); if (context_handle != GSS_C_NO_CONTEXT) gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER); if(scred) ber_bvfree(scred); return status; }
static krb5_error_code parse_setpw_reply(krb5_context context, bool use_tcp, krb5_auth_context auth_context, krb5_data *packet) { krb5_data ap_rep; char *p; int vnum, ret, res_code; krb5_data cipherresult; krb5_data clearresult; krb5_ap_rep_enc_part *ap_rep_enc; krb5_replay_data replay; unsigned int msg_length = packet->length; if (packet->length < (use_tcp ? 8 : 4)) { return KRB5KRB_AP_ERR_MODIFIED; } p = (char *)packet->data; /* ** see if it is an error */ if (krb5_is_krb_error(packet)) { ret = handle_krberror_packet(context, packet); if (ret) { return ret; } } /* tcp... */ if (use_tcp) { msg_length -= 4; if (RIVAL(p, 0) != msg_length) { DEBUG(1,("Bad TCP packet length (%d/%d) from kpasswd server\n", RIVAL(p, 0), msg_length)); return KRB5KRB_AP_ERR_MODIFIED; } p += 4; } if (RSVAL(p, 0) != msg_length) { DEBUG(1,("Bad packet length (%d/%d) from kpasswd server\n", RSVAL(p, 0), msg_length)); return KRB5KRB_AP_ERR_MODIFIED; } p += 2; vnum = RSVAL(p, 0); p += 2; /* FIXME: According to standard there is only one type of reply */ if (vnum != KRB5_KPASSWD_VERS_SETPW && vnum != KRB5_KPASSWD_VERS_SETPW_ALT && vnum != KRB5_KPASSWD_VERS_CHANGEPW) { DEBUG(1,("Bad vnum (%d) from kpasswd server\n", vnum)); return KRB5KDC_ERR_BAD_PVNO; } ap_rep.length = RSVAL(p, 0); p += 2; if (p + ap_rep.length >= (char *)packet->data + packet->length) { DEBUG(1,("ptr beyond end of packet from kpasswd server\n")); return KRB5KRB_AP_ERR_MODIFIED; } if (ap_rep.length == 0) { DEBUG(1,("got unencrypted setpw result?!\n")); return KRB5KRB_AP_ERR_MODIFIED; } /* verify ap_rep */ ap_rep.data = p; p += ap_rep.length; ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc); if (ret) { DEBUG(1,("failed to rd setpw reply (%s)\n", error_message(ret))); return KRB5KRB_AP_ERR_MODIFIED; } krb5_free_ap_rep_enc_part(context, ap_rep_enc); cipherresult.data = p; cipherresult.length = ((char *)packet->data + packet->length) - p; ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult, &replay); if (ret) { DEBUG(1,("failed to decrypt setpw reply (%s)\n", error_message(ret))); return KRB5KRB_AP_ERR_MODIFIED; } if (clearresult.length < 2) { free(clearresult.data); ret = KRB5KRB_AP_ERR_MODIFIED; return KRB5KRB_AP_ERR_MODIFIED; } p = (char *)clearresult.data; res_code = RSVAL(p, 0); free(clearresult.data); if ((res_code < KRB5_KPASSWD_SUCCESS) || (res_code > KRB5_KPASSWD_ETYPE_NOSUPP)) { return KRB5KRB_AP_ERR_MODIFIED; } if (res_code == KRB5_KPASSWD_SUCCESS) { return 0; } else { const char *errstr; setpw_result_code_string(context, res_code, &errstr); DEBUG(1, ("Error changing password: %s (%d)\n", errstr, res_code)); return kpasswd_err_to_krb5_err(res_code); } }
bool receive_getdc_response(TALLOC_CTX *mem_ctx, struct sockaddr_storage *dc_ss, const char *domain_name, uint32_t *nt_version, const char **dc_name, struct netlogon_samlogon_response **_r) { struct packet_struct *packet; const char *my_mailslot = NULL; struct in_addr dc_ip; DATA_BLOB blob; struct netlogon_samlogon_response r; union dgram_message_body p; enum ndr_err_code ndr_err; NTSTATUS status; const char *returned_dc = NULL; const char *returned_domain = NULL; if (dc_ss->ss_family != AF_INET) { return false; } dc_ip = ((struct sockaddr_in *)dc_ss)->sin_addr; my_mailslot = mailslot_name(mem_ctx, dc_ip); if (!my_mailslot) { return false; } packet = receive_unexpected(DGRAM_PACKET, 0, my_mailslot); if (packet == NULL) { DEBUG(5, ("Did not receive packet for %s\n", my_mailslot)); return False; } DEBUG(5, ("Received packet for %s\n", my_mailslot)); blob = data_blob_const(packet->packet.dgram.data, packet->packet.dgram.datasize); if (blob.length < 4) { DEBUG(0,("invalid length: %d\n", (int)blob.length)); return false; } if (RIVAL(blob.data,0) != DGRAM_SMB) { DEBUG(0,("invalid packet\n")); return false; } blob.data += 4; blob.length -= 4; ndr_err = ndr_pull_union_blob_all(&blob, mem_ctx, &p, DGRAM_SMB, (ndr_pull_flags_fn_t)ndr_pull_dgram_smb_packet); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { DEBUG(0,("failed to parse packet\n")); return false; } if (p.smb.smb_command != SMB_TRANSACTION) { DEBUG(0,("invalid smb_command: %d\n", p.smb.smb_command)); return false; } if (DEBUGLEVEL >= 10) { NDR_PRINT_DEBUG(dgram_smb_packet, &p); } blob = p.smb.body.trans.data; ZERO_STRUCT(r); status = pull_netlogon_samlogon_response(&blob, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { return false; } map_netlogon_samlogon_response(&r); /* do we still need this ? */ *nt_version = r.ntver; returned_domain = r.data.nt5_ex.domain; returned_dc = r.data.nt5_ex.pdc_name; if (!strequal(returned_domain, domain_name)) { DEBUG(3, ("GetDC: Expected domain %s, got %s\n", domain_name, returned_domain)); return false; } *dc_name = talloc_strdup(mem_ctx, returned_dc); if (!*dc_name) { return false; } if (**dc_name == '\\') *dc_name += 1; if (**dc_name == '\\') *dc_name += 1; if (_r) { *_r = (struct netlogon_samlogon_response *)talloc_memdup( mem_ctx, &r, sizeof(struct netlogon_samlogon_response)); if (!*_r) { return false; } } DEBUG(10, ("GetDC gave name %s for domain %s\n", *dc_name, returned_domain)); return True; }
static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, struct tevent_context *ev, const DATA_BLOB in, DATA_BLOB *out) { struct gensec_gssapi_state *gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE; OM_uint32 maj_stat, min_stat; OM_uint32 min_stat2; gss_buffer_desc input_token = { 0, NULL }; gss_buffer_desc output_token = { 0, NULL }; gss_OID gss_oid_p = NULL; OM_uint32 time_req = 0; OM_uint32 time_rec = 0; struct timeval tv; time_req = gensec_setting_int(gensec_security->settings, "gensec_gssapi", "requested_life_time", time_req); input_token.length = in.length; input_token.value = in.data; switch (gensec_gssapi_state->sasl_state) { case STAGE_GSS_NEG: { switch (gensec_security->gensec_role) { case GENSEC_CLIENT: { #ifdef SAMBA4_USES_HEIMDAL struct gsskrb5_send_to_kdc send_to_kdc; krb5_error_code ret; #endif nt_status = gensec_gssapi_client_creds(gensec_security, ev); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } #ifdef SAMBA4_USES_HEIMDAL send_to_kdc.func = smb_krb5_send_and_recv_func; send_to_kdc.ptr = ev; min_stat = gsskrb5_set_send_to_kdc(&send_to_kdc); if (min_stat) { DEBUG(1,("gensec_gssapi_update: gsskrb5_set_send_to_kdc failed\n")); return NT_STATUS_INTERNAL_ERROR; } #endif maj_stat = gss_init_sec_context(&min_stat, gensec_gssapi_state->client_cred->creds, &gensec_gssapi_state->gssapi_context, gensec_gssapi_state->server_name, gensec_gssapi_state->gss_oid, gensec_gssapi_state->gss_want_flags, time_req, gensec_gssapi_state->input_chan_bindings, &input_token, &gss_oid_p, &output_token, &gensec_gssapi_state->gss_got_flags, /* ret flags */ &time_rec); if (gss_oid_p) { gensec_gssapi_state->gss_oid = gss_oid_p; } #ifdef SAMBA4_USES_HEIMDAL send_to_kdc.func = smb_krb5_send_and_recv_func; send_to_kdc.ptr = NULL; ret = gsskrb5_set_send_to_kdc(&send_to_kdc); if (ret) { DEBUG(1,("gensec_gssapi_update: gsskrb5_set_send_to_kdc failed\n")); return NT_STATUS_INTERNAL_ERROR; } #endif break; } case GENSEC_SERVER: { maj_stat = gss_accept_sec_context(&min_stat, &gensec_gssapi_state->gssapi_context, gensec_gssapi_state->server_cred->creds, &input_token, gensec_gssapi_state->input_chan_bindings, &gensec_gssapi_state->client_name, &gss_oid_p, &output_token, &gensec_gssapi_state->gss_got_flags, &time_rec, &gensec_gssapi_state->delegated_cred_handle); if (gss_oid_p) { gensec_gssapi_state->gss_oid = gss_oid_p; } break; } default: return NT_STATUS_INVALID_PARAMETER; } gensec_gssapi_state->gss_exchange_count++; if (maj_stat == GSS_S_COMPLETE) { *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); gss_release_buffer(&min_stat2, &output_token); if (gensec_gssapi_state->gss_got_flags & GSS_C_DELEG_FLAG && gensec_gssapi_state->delegated_cred_handle != GSS_C_NO_CREDENTIAL) { DEBUG(5, ("gensec_gssapi: credentials were delegated\n")); } else { DEBUG(5, ("gensec_gssapi: NO credentials were delegated\n")); } tv = timeval_current_ofs(time_rec, 0); gensec_gssapi_state->expire_time = timeval_to_nttime(&tv); /* We may have been invoked as SASL, so there * is more work to do */ if (gensec_gssapi_state->sasl) { gensec_gssapi_state->sasl_state = STAGE_SASL_SSF_NEG; return NT_STATUS_MORE_PROCESSING_REQUIRED; } else { gensec_gssapi_state->sasl_state = STAGE_DONE; if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { DEBUG(5, ("GSSAPI Connection will be cryptographically sealed\n")); } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(5, ("GSSAPI Connection will be cryptographically signed\n")); } else { DEBUG(5, ("GSSAPI Connection will have no cryptographic protection\n")); } return NT_STATUS_OK; } } else if (maj_stat == GSS_S_CONTINUE_NEEDED) { *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); gss_release_buffer(&min_stat2, &output_token); return NT_STATUS_MORE_PROCESSING_REQUIRED; } else if (maj_stat == GSS_S_CONTEXT_EXPIRED) { gss_cred_id_t creds = NULL; gss_name_t name; gss_buffer_desc buffer; OM_uint32 lifetime = 0; gss_cred_usage_t usage; const char *role = NULL; DEBUG(0, ("GSS %s Update(krb5)(%d) Update failed, credentials expired during GSSAPI handshake!\n", role, gensec_gssapi_state->gss_exchange_count)); switch (gensec_security->gensec_role) { case GENSEC_CLIENT: creds = gensec_gssapi_state->client_cred->creds; role = "client"; break; case GENSEC_SERVER: creds = gensec_gssapi_state->server_cred->creds; role = "server"; break; } maj_stat = gss_inquire_cred(&min_stat, creds, &name, &lifetime, &usage, NULL); if (maj_stat == GSS_S_COMPLETE) { const char *usage_string = NULL; switch (usage) { case GSS_C_BOTH: usage_string = "GSS_C_BOTH"; break; case GSS_C_ACCEPT: usage_string = "GSS_C_ACCEPT"; break; case GSS_C_INITIATE: usage_string = "GSS_C_INITIATE"; break; } maj_stat = gss_display_name(&min_stat, name, &buffer, NULL); if (maj_stat) { buffer.value = NULL; buffer.length = 0; } if (lifetime > 0) { DEBUG(0, ("GSSAPI gss_inquire_cred indicates expiry of %*.*s in %u sec for %s\n", (int)buffer.length, (int)buffer.length, (char *)buffer.value, lifetime, usage_string)); } else { DEBUG(0, ("GSSAPI gss_inquire_cred indicates %*.*s has already expired for %s\n", (int)buffer.length, (int)buffer.length, (char *)buffer.value, usage_string)); } gss_release_buffer(&min_stat, &buffer); gss_release_name(&min_stat, &name); } else if (maj_stat != GSS_S_COMPLETE) { DEBUG(0, ("inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n", gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); } return NT_STATUS_INVALID_PARAMETER; } else if (smb_gss_oid_equal(gensec_gssapi_state->gss_oid, gss_mech_krb5)) { switch (min_stat) { case KRB5KRB_AP_ERR_TKT_NYV: DEBUG(1, ("Error with ticket to contact %s: possible clock skew between us and the KDC or target server: %s\n", gensec_gssapi_state->target_principal, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_TIME_DIFFERENCE_AT_DC; /* Make SPNEGO ignore us, we can't go any further here */ case KRB5KRB_AP_ERR_TKT_EXPIRED: DEBUG(1, ("Error with ticket to contact %s: ticket is expired, possible clock skew between us and the KDC or target server: %s\n", gensec_gssapi_state->target_principal, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ case KRB5_KDC_UNREACH: DEBUG(3, ("Cannot reach a KDC we require in order to obtain a ticket to %s: %s\n", gensec_gssapi_state->target_principal, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_NO_LOGON_SERVERS; /* Make SPNEGO ignore us, we can't go any further here */ case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN: DEBUG(3, ("Server %s is not registered with our KDC: %s\n", gensec_gssapi_state->target_principal, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ case KRB5KRB_AP_ERR_MSG_TYPE: /* garbage input, possibly from the auto-mech detection */ return NT_STATUS_INVALID_PARAMETER; default: DEBUG(1, ("GSS %s Update(krb5)(%d) Update failed: %s\n", gensec_security->gensec_role == GENSEC_CLIENT ? "client" : "server", gensec_gssapi_state->gss_exchange_count, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_LOGON_FAILURE; } } else { DEBUG(1, ("GSS %s Update(%d) failed: %s\n", gensec_security->gensec_role == GENSEC_CLIENT ? "client" : "server", gensec_gssapi_state->gss_exchange_count, gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_LOGON_FAILURE; } break; } /* These last two stages are only done if we were invoked as SASL */ case STAGE_SASL_SSF_NEG: { switch (gensec_security->gensec_role) { case GENSEC_CLIENT: { uint8_t maxlength_proposed[4]; uint8_t maxlength_accepted[4]; uint8_t security_supported; int conf_state; gss_qop_t qop_state; input_token.length = in.length; input_token.value = in.data; /* As a client, we have just send a * zero-length blob to the server (after the * normal GSSAPI exchange), and it has replied * with it's SASL negotiation */ maj_stat = gss_unwrap(&min_stat, gensec_gssapi_state->gssapi_context, &input_token, &output_token, &conf_state, &qop_state); if (GSS_ERROR(maj_stat)) { DEBUG(1, ("gensec_gssapi_update: GSS UnWrap of SASL protection negotiation failed: %s\n", gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_ACCESS_DENIED; } if (output_token.length < 4) { return NT_STATUS_INVALID_PARAMETER; } memcpy(maxlength_proposed, output_token.value, 4); gss_release_buffer(&min_stat, &output_token); /* first byte is the proposed security */ security_supported = maxlength_proposed[0]; maxlength_proposed[0] = '\0'; /* Rest is the proposed max wrap length */ gensec_gssapi_state->max_wrap_buf_size = MIN(RIVAL(maxlength_proposed, 0), gensec_gssapi_state->max_wrap_buf_size); gensec_gssapi_state->sasl_protection = 0; if (security_supported & NEG_SEAL) { if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { gensec_gssapi_state->sasl_protection |= NEG_SEAL; } } if (security_supported & NEG_SIGN) { if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { gensec_gssapi_state->sasl_protection |= NEG_SIGN; } } if (security_supported & NEG_NONE) { gensec_gssapi_state->sasl_protection |= NEG_NONE; } if (gensec_gssapi_state->sasl_protection == 0) { DEBUG(1, ("Remote server does not support unprotected connections\n")); return NT_STATUS_ACCESS_DENIED; } /* Send back the negotiated max length */ RSIVAL(maxlength_accepted, 0, gensec_gssapi_state->max_wrap_buf_size); maxlength_accepted[0] = gensec_gssapi_state->sasl_protection; input_token.value = maxlength_accepted; input_token.length = sizeof(maxlength_accepted); maj_stat = gss_wrap(&min_stat, gensec_gssapi_state->gssapi_context, false, GSS_C_QOP_DEFAULT, &input_token, &conf_state, &output_token); if (GSS_ERROR(maj_stat)) { DEBUG(1, ("GSS Update(SSF_NEG): GSS Wrap failed: %s\n", gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_ACCESS_DENIED; } *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); gss_release_buffer(&min_stat, &output_token); /* quirk: This changes the value that gensec_have_feature returns, to be that after SASL negotiation */ gensec_gssapi_state->sasl_state = STAGE_DONE; if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { DEBUG(3, ("SASL/GSSAPI Connection to server will be cryptographically sealed\n")); } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(3, ("SASL/GSSAPI Connection to server will be cryptographically signed\n")); } else { DEBUG(3, ("SASL/GSSAPI Connection to server will have no cryptographically protection\n")); } return NT_STATUS_OK; } case GENSEC_SERVER: { uint8_t maxlength_proposed[4]; uint8_t security_supported = 0x0; int conf_state; /* As a server, we have just been sent a zero-length blob (note this, but it isn't fatal) */ if (in.length != 0) { DEBUG(1, ("SASL/GSSAPI: client sent non-zero length starting SASL negotiation!\n")); } /* Give the client some idea what we will support */ RSIVAL(maxlength_proposed, 0, gensec_gssapi_state->max_wrap_buf_size); /* first byte is the proposed security */ maxlength_proposed[0] = '\0'; gensec_gssapi_state->sasl_protection = 0; if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { security_supported |= NEG_SEAL; } if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { security_supported |= NEG_SIGN; } if (security_supported == 0) { /* If we don't support anything, this must be 0 */ RSIVAL(maxlength_proposed, 0, 0x0); } /* TODO: We may not wish to support this */ security_supported |= NEG_NONE; maxlength_proposed[0] = security_supported; input_token.value = maxlength_proposed; input_token.length = sizeof(maxlength_proposed); maj_stat = gss_wrap(&min_stat, gensec_gssapi_state->gssapi_context, false, GSS_C_QOP_DEFAULT, &input_token, &conf_state, &output_token); if (GSS_ERROR(maj_stat)) { DEBUG(1, ("GSS Update(SSF_NEG): GSS Wrap failed: %s\n", gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_ACCESS_DENIED; } *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length); gss_release_buffer(&min_stat, &output_token); gensec_gssapi_state->sasl_state = STAGE_SASL_SSF_ACCEPT; return NT_STATUS_MORE_PROCESSING_REQUIRED; } default: return NT_STATUS_INVALID_PARAMETER; } } /* This is s server-only stage */ case STAGE_SASL_SSF_ACCEPT: { uint8_t maxlength_accepted[4]; uint8_t security_accepted; int conf_state; gss_qop_t qop_state; input_token.length = in.length; input_token.value = in.data; maj_stat = gss_unwrap(&min_stat, gensec_gssapi_state->gssapi_context, &input_token, &output_token, &conf_state, &qop_state); if (GSS_ERROR(maj_stat)) { DEBUG(1, ("gensec_gssapi_update: GSS UnWrap of SASL protection negotiation failed: %s\n", gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_ACCESS_DENIED; } if (output_token.length < 4) { return NT_STATUS_INVALID_PARAMETER; } memcpy(maxlength_accepted, output_token.value, 4); gss_release_buffer(&min_stat, &output_token); /* first byte is the proposed security */ security_accepted = maxlength_accepted[0]; maxlength_accepted[0] = '\0'; /* Rest is the proposed max wrap length */ gensec_gssapi_state->max_wrap_buf_size = MIN(RIVAL(maxlength_accepted, 0), gensec_gssapi_state->max_wrap_buf_size); gensec_gssapi_state->sasl_protection = 0; if (security_accepted & NEG_SEAL) { if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { DEBUG(1, ("Remote client wanted seal, but gensec refused\n")); return NT_STATUS_ACCESS_DENIED; } gensec_gssapi_state->sasl_protection |= NEG_SEAL; } if (security_accepted & NEG_SIGN) { if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(1, ("Remote client wanted sign, but gensec refused\n")); return NT_STATUS_ACCESS_DENIED; } gensec_gssapi_state->sasl_protection |= NEG_SIGN; } if (security_accepted & NEG_NONE) { gensec_gssapi_state->sasl_protection |= NEG_NONE; } /* quirk: This changes the value that gensec_have_feature returns, to be that after SASL negotiation */ gensec_gssapi_state->sasl_state = STAGE_DONE; if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { DEBUG(5, ("SASL/GSSAPI Connection from client will be cryptographically sealed\n")); } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) { DEBUG(5, ("SASL/GSSAPI Connection from client will be cryptographically signed\n")); } else { DEBUG(5, ("SASL/GSSAPI Connection from client will have no cryptographic protection\n")); } *out = data_blob(NULL, 0); return NT_STATUS_OK; } default: return NT_STATUS_INVALID_PARAMETER; } }
static bool parse_getdc_response( struct packet_struct *packet, TALLOC_CTX *mem_ctx, const char *domain_name, uint32_t *nt_version, const char **dc_name, struct netlogon_samlogon_response **samlogon_response) { DATA_BLOB blob; struct netlogon_samlogon_response *r; union dgram_message_body p; enum ndr_err_code ndr_err; NTSTATUS status; const char *returned_dc = NULL; const char *returned_domain = NULL; blob = data_blob_const(packet->packet.dgram.data, packet->packet.dgram.datasize); if (blob.length < 4) { DEBUG(1, ("invalid length: %d\n", (int)blob.length)); return false; } if (RIVAL(blob.data,0) != DGRAM_SMB) { DEBUG(1, ("invalid packet\n")); return false; } blob.data += 4; blob.length -= 4; ndr_err = ndr_pull_union_blob_all(&blob, mem_ctx, &p, DGRAM_SMB, (ndr_pull_flags_fn_t)ndr_pull_dgram_smb_packet); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { DEBUG(1, ("failed to parse packet\n")); return false; } if (p.smb.smb_command != SMB_TRANSACTION) { DEBUG(1, ("invalid smb_command: %d\n", p.smb.smb_command)); return false; } if (DEBUGLEVEL >= 10) { NDR_PRINT_DEBUG(dgram_smb_packet, &p); } blob = p.smb.body.trans.data; r = talloc_zero(mem_ctx, struct netlogon_samlogon_response); if (!r) { return false; } status = pull_netlogon_samlogon_response(&blob, r, r); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(r); return false; } map_netlogon_samlogon_response(r); /* do we still need this ? */ *nt_version = r->ntver; returned_domain = r->data.nt5_ex.domain_name; returned_dc = r->data.nt5_ex.pdc_name; if (!strequal(returned_domain, domain_name)) { DEBUG(3, ("GetDC: Expected domain %s, got %s\n", domain_name, returned_domain)); TALLOC_FREE(r); return false; } if (*returned_dc == '\\') returned_dc += 1; if (*returned_dc == '\\') returned_dc += 1; *dc_name = talloc_strdup(mem_ctx, returned_dc); if (!*dc_name) { TALLOC_FREE(r); return false; } if (samlogon_response) { *samlogon_response = r; } else { TALLOC_FREE(r); } DEBUG(10, ("GetDC gave name %s for domain %s\n", *dc_name, returned_domain)); return True; }