/* Establish a connection up to the ISO layer */ RD_BOOL iso_connect(char *server, char *username, RD_BOOL reconnect, uint32 * selected_protocol) { STREAM s; uint8 code; g_negotiate_rdp_protocol = True; retry: *selected_protocol = PROTOCOL_RDP; code = 0; if (!tcp_connect(server)) return False; if (reconnect) { iso_send_msg(ISO_PDU_CR); } else { iso_send_connection_request(username); } s = iso_recv_msg(&code, NULL); if (s == NULL) return False; if (code != ISO_PDU_CC) { error("expected CC, got 0x%x\n", code); tcp_disconnect(); return False; } if (g_rdp_version >= RDP_V5 && s_check_rem(s, 8)) { /* handle RDP_NEG_REQ response */ const char *reason = NULL; uint8 type = 0, flags = 0; uint16 length = 0; uint32 data = 0; in_uint8(s, type); in_uint8(s, flags); in_uint16(s, length); in_uint32(s, data); if (type == RDP_NEG_FAILURE) { switch (data) { case SSL_REQUIRED_BY_SERVER: reason = "SSL required by server"; break; case SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER: reason = "SSL with user authentication required by server"; break; case SSL_NOT_ALLOWED_BY_SERVER: reason = "SSL not allowed by server"; break; case SSL_CERT_NOT_ON_SERVER: reason = "SSL certificated not on server"; break; case INCONSISTENT_FLAGS: reason = "inconsistent flags"; break; case HYBRID_REQUIRED_BY_SERVER: reason = "hybrid authentication (CredSSP) required by server"; break; default: reason = "unknown reason"; } tcp_disconnect(); warning("RDP protocol negotiation failed with reason: %s (error 0x%x),\n", reason, data); warning("retrying without negotiation using plain RDP protocol.\n"); g_negotiate_rdp_protocol = False; goto retry; } if (type != RDP_NEG_RSP) { tcp_disconnect(); error("expected RDP_NEG_RSP, got type = 0x%x\n", type); warning("retrying without negotiation using plain RDP protocol.\n"); g_negotiate_rdp_protocol = False; goto retry; } /* handle negotiation response */ if (data == PROTOCOL_SSL) { DEBUGMSG(1,(L"iso_connect: negotiation: PROTOCOL_SSL\n")); if (!tcp_tls_connect()) { tcp_disconnect(); DEBUGMSG(1,(L"iso_connect: negotiation: PROTOCOL_SSL FAILED\n")); return False; } /* do not use encryption when using TLS */ g_encryption = False; } else if (data != PROTOCOL_RDP) { tcp_disconnect(); error("unexpected protocol in neqotiation response, got data = 0x%x.\n", data); return False; } *selected_protocol = data; } return True; }
RD_BOOL cssp_connect(char *server, char *user, char *domain, char *password, STREAM s) { OM_uint32 actual_time; gss_cred_id_t cred; gss_buffer_desc input_tok, output_tok; gss_name_t target_name; OM_uint32 major_status, minor_status; int context_established = 0; gss_ctx_id_t gss_ctx; gss_OID desired_mech = &_gss_spnego_krb5_mechanism_oid_desc; STREAM ts_creds; struct stream token = { 0 }; struct stream pubkey = { 0 }; struct stream pubkey_cmp = { 0 }; // Verify that system gss support spnego if (!cssp_gss_mech_available(desired_mech)) { warning("CredSSP: System doesn't have support for desired authentication mechanism.\n"); return False; } // Get service name if (!cssp_gss_get_service_name(server, &target_name)) { warning("CredSSP: Failed to get target service name.\n"); return False; } // Establish tls connection to server if (!tcp_tls_connect()) { warning("CredSSP: Failed to establish TLS connection.\n"); return False; } tcp_tls_get_server_pubkey(&pubkey); #ifdef WITH_DEBUG_CREDSSP streamsave(&pubkey, "PubKey.raw"); #endif // Enter the spnego loop OM_uint32 actual_services; gss_OID actual_mech; struct stream blob = { 0 }; gss_ctx = GSS_C_NO_CONTEXT; cred = GSS_C_NO_CREDENTIAL; input_tok.length = 0; output_tok.length = 0; minor_status = 0; int i = 0; do { major_status = gss_init_sec_context(&minor_status, cred, &gss_ctx, target_name, desired_mech, GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG, GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, &input_tok, &actual_mech, &output_tok, &actual_services, &actual_time); if (GSS_ERROR(major_status)) { if (i == 0) error("CredSSP: Initialize failed, do you have correct kerberos tgt initialized ?\n"); else error("CredSSP: Negotiation failed.\n"); #ifdef WITH_DEBUG_CREDSSP cssp_gss_report_error(GSS_C_GSS_CODE, "CredSSP: SPNEGO negotiation failed.", major_status, minor_status); #endif goto bail_out; } // validate required services if (!(actual_services & GSS_C_CONF_FLAG)) { error("CredSSP: Confidiality service required but is not available.\n"); goto bail_out; } // Send token to server if (output_tok.length != 0) { if (output_tok.length > token.size) s_realloc(&token, output_tok.length); s_reset(&token); out_uint8p(&token, output_tok.value, output_tok.length); s_mark_end(&token); if (!cssp_send_tsrequest(&token, NULL, NULL)) goto bail_out; (void) gss_release_buffer(&minor_status, &output_tok); } // Read token from server if (major_status & GSS_S_CONTINUE_NEEDED) { (void) gss_release_buffer(&minor_status, &input_tok); if (!cssp_read_tsrequest(&token, NULL)) goto bail_out; input_tok.value = token.data; input_tok.length = s_length(&token); } else { // Send encrypted pubkey for verification to server context_established = 1; if (!cssp_gss_wrap(gss_ctx, &pubkey, &blob)) goto bail_out; if (!cssp_send_tsrequest(NULL, NULL, &blob)) goto bail_out; context_established = 1; } i++; } while (!context_established); // read tsrequest response and decrypt for public key validation if (!cssp_read_tsrequest(NULL, &blob)) goto bail_out; if (!cssp_gss_unwrap(gss_ctx, &blob, &pubkey_cmp)) goto bail_out; pubkey_cmp.data[0] -= 1; // validate public key if (memcmp(pubkey.data, pubkey_cmp.data, s_length(&pubkey)) != 0) { error("CredSSP: Cannot guarantee integrity of server connection, MITM ? " "(public key data mismatch)\n"); goto bail_out; } // Send TSCredentials ts_creds = cssp_encode_tscredentials(user, password, domain); if (!cssp_gss_wrap(gss_ctx, ts_creds, &blob)) goto bail_out; s_free(ts_creds); if (!cssp_send_tsrequest(NULL, &blob, NULL)) goto bail_out; return True; bail_out: xfree(token.data); return False; }
/* Establish a connection up to the ISO layer */ RD_BOOL iso_connect(char *server, char *username, char *domain, char *password, RD_BOOL reconnect, uint32 * selected_protocol) { STREAM s; uint8 code; uint32 neg_proto; g_negotiate_rdp_protocol = True; neg_proto = PROTOCOL_SSL; #ifdef WITH_CREDSSP if (!g_use_password_as_pin) neg_proto |= PROTOCOL_HYBRID; else if (g_sc_csp_name || g_sc_reader_name || g_sc_card_name || g_sc_container_name) neg_proto |= PROTOCOL_HYBRID; else warning("Disables CredSSP due to missing smartcard information for SSO.\n"); #endif retry: *selected_protocol = PROTOCOL_RDP; code = 0; if (!tcp_connect(server)) return False; iso_send_connection_request(username, neg_proto); s = iso_recv_msg(&code, NULL); if (s == NULL) return False; if (code != ISO_PDU_CC) { error("expected CC, got 0x%x\n", code); tcp_disconnect(); return False; } if (g_rdp_version >= RDP_V5 && s_check_rem(s, 8)) { /* handle RDP_NEG_REQ response */ const char *reason = NULL; uint8 type = 0, flags = 0; uint16 length = 0; uint32 data = 0; in_uint8(s, type); in_uint8(s, flags); in_uint16(s, length); in_uint32(s, data); if (type == RDP_NEG_FAILURE) { RD_BOOL retry_without_neg = False; switch (data) { case SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER: reason = "SSL with user authentication required by server"; break; case SSL_NOT_ALLOWED_BY_SERVER: reason = "SSL not allowed by server"; retry_without_neg = True; break; case SSL_CERT_NOT_ON_SERVER: reason = "no valid authentication certificate on server"; retry_without_neg = True; break; case INCONSISTENT_FLAGS: reason = "inconsistent negotiation flags"; break; case SSL_REQUIRED_BY_SERVER: reason = "SSL required by server"; break; case HYBRID_REQUIRED_BY_SERVER: reason = "CredSSP required by server"; break; default: reason = "unknown reason"; } tcp_disconnect(); if (retry_without_neg) { fprintf(stderr, "Failed to negotiate protocol, retrying with plain RDP.\n"); g_negotiate_rdp_protocol = False; goto retry; } fprintf(stderr, "Failed to connect, %s.\n", reason); return False; } if (type != RDP_NEG_RSP) { tcp_disconnect(); error("Expected RDP_NEG_RSP, got type = 0x%x\n", type); return False; } /* handle negotiation response */ if (data == PROTOCOL_SSL) { if (!tcp_tls_connect()) { /* failed to connect using cssp, let retry with plain TLS */ tcp_disconnect(); neg_proto = PROTOCOL_RDP; goto retry; } /* do not use encryption when using TLS */ g_encryption = False; fprintf(stderr, "Connection established using SSL.\n"); } #ifdef WITH_CREDSSP else if (data == PROTOCOL_HYBRID) { if (!cssp_connect(server, username, domain, password, s)) { /* failed to connect using cssp, let retry with plain TLS */ tcp_disconnect(); neg_proto = PROTOCOL_SSL; goto retry; } /* do not use encryption when using TLS */ fprintf(stderr, "Connection established using CredSSP.\n"); g_encryption = False; } #endif else if (data == PROTOCOL_RDP) { fprintf(stderr, "Connection established using plain RDP.\n"); } else if (data != PROTOCOL_RDP) { tcp_disconnect(); error("Unexpected protocol in negotiation response, got data = 0x%x.\n", data); return False; } *selected_protocol = data; } return True; }