/* Process a system pointer PDU */ void process_system_pointer_pdu(STREAM s) { uint16 system_pointer_type; in_uint16(s, system_pointer_type); switch (system_pointer_type) { case RDP_NULL_POINTER: ui_set_null_cursor(); break; default: unimpl("System pointer message 0x%x\n", system_pointer_type); } }
/* Process an licence issue packet */ static void licence_process_issue(STREAM s) { RC4_KEY crypt_key; uint32 length; uint16 check; in_uint8s(s, 2); /* 3d 45 - unknown */ in_uint16_le(s, length); if (!s_check_rem(s, length)) return; RC4_set_key(&crypt_key, 16, licence_key); RC4(&crypt_key, length, s->p, s->p); in_uint16(s, check); if (check != 0) return; licence_issued = True; save_licence(s->p, length-2); }
/* Process an licence issue packet */ static void licence_process_issue(STREAM s) { void * crypt_key; uint32 length; uint16 check; int i; in_uint8s(s, 2); /* 3d 45 - unknown */ in_uint16_le(s, length); if (!s_check_rem(s, length)) return; crypt_key = ssl_rc4_info_create(); ssl_rc4_set_key(crypt_key, (char *)g_licence_key, 16); ssl_rc4_crypt(crypt_key, (char *)s->p, (char *)s->p, length); ssl_rc4_info_delete(crypt_key); in_uint16(s, check); if (check != 0) return; g_licence_issued = True; in_uint8s(s, 2); /* pad */ /* advance to fourth string */ length = 0; for (i = 0; i < 4; i++) { in_uint8s(s, length); in_uint32_le(s, length); if (!s_check_rem(s, length)) return; } g_licence_issued = True; save_licence(s->p, length); }
/* Process an licence issue packet */ static void licence_process_issue(RDPCLIENT * This, STREAM s) { RC4_KEY crypt_key; uint32 length; uint16 check; int i; in_uint8s(s, 2); /* 3d 45 - unknown */ in_uint16_le(s, length); if (!s_check_rem(s, length)) return; RC4_set_key(&crypt_key, 16, This->licence.key); RC4(&crypt_key, length, s->p, s->p); in_uint16(s, check); if (check != 0) return; This->licence_issued = True; in_uint8s(s, 2); /* pad */ /* advance to fourth string */ length = 0; for (i = 0; i < 4; i++) { in_uint8s(s, length); in_uint32_le(s, length); if (!s_check_rem(s, length)) return; } This->licence_issued = True; save_licence(This, s->p, length); }
/* Process an licence issue packet */ static void licence_process_issue(RDConnectionRef conn, RDStreamRef s) { RC4_KEY crypt_key; uint32 length; uint16 check; int i; in_uint8s(s, 2); /* 3d 45 - unknown */ in_uint16_le(s, length); if (!s_check_rem(s, length)) return; RC4_set_key(&crypt_key, 16, conn->licenseKey); RC4(&crypt_key, length, s->p, s->p); in_uint16(s, check); if (check != 0) return; conn->licenseIssued = True; in_uint8s(s, 2); /* pad */ /* advance to fourth string */ length = 0; for (i = 0; i < 4; i++) { in_uint8s(s, length); in_uint32_le(s, length); if (!s_check_rem(s, length)) return; } conn->licenseIssued = True; save_licence(s->p, length); }
/* Process data PDU */ static BOOL process_data_pdu(STREAM s, uint32 * ext_disc_reason) { uint8 data_pdu_type; uint8 ctype; uint16 clen; uint32 len; uint32 roff, rlen; struct stream *ns = &(g_mppc_dict.ns); in_uint8s(s, 6); /* shareid, pad, streamid */ in_uint16(s, len); in_uint8(s, data_pdu_type); in_uint8(s, ctype); in_uint16(s, clen); clen -= 18; if (ctype & RDP_MPPC_COMPRESSED) { if (len > RDP_MPPC_DICT_SIZE) error("error decompressed packet size exceeds max\n"); if (mppc_expand(s->p, clen, ctype, &roff, &rlen) == -1) error("error while decompressing packet\n"); /* len -= 18; */ /* allocate memory and copy the uncompressed data into the temporary stream */ ns->data = (uint8 *) xrealloc(ns->data, rlen); memcpy((ns->data), (unsigned char *) (g_mppc_dict.hist + roff), rlen); ns->size = rlen; ns->end = (ns->data + ns->size); ns->p = ns->data; ns->rdp_hdr = ns->p; s = ns; } switch (data_pdu_type) { case RDP_DATA_PDU_UPDATE: process_update_pdu(s); break; case RDP_DATA_PDU_CONTROL: DEBUG(("Received Control PDU\n")); break; case RDP_DATA_PDU_SYNCHRONISE: DEBUG(("Received Sync PDU\n")); break; case RDP_DATA_PDU_POINTER: process_pointer_pdu(s); break; case RDP_DATA_PDU_BELL: ui_bell(); break; case RDP_DATA_PDU_LOGON: DEBUG(("Received Logon PDU\n")); /* User logged on */ break; case RDP_DATA_PDU_DISCONNECT: process_disconnect_pdu(s, ext_disc_reason); return True; default: unimpl("data PDU %d\n", data_pdu_type); } return False; }
void save_licence(unsigned char *data, int length) { char *fpath; /* file path for licence */ char *fname, *fnamewrk; /* file name for licence .inkl path. */ char *home; uint32 y; struct flock fnfl; int fnfd, fnwrkfd, i, wlen; struct stream s, *s_ptr; uint32 len; /* Construct a stream, so that we can use macros to extract the * licence. */ s_ptr = &s; s_ptr->p = data; /* Skip first two bytes */ in_uint16(s_ptr, len); /* Skip three strings */ for (i = 0; i < 3; i++) { in_uint32(s_ptr, len); s_ptr->p += len; /* Make sure that we won't be past the end of data after * reading the next length value */ if ((s_ptr->p) + 4 > data + length) { printf("Error in parsing licence key.\n"); printf("Strings %d end value %x > supplied length (%x)\n", i, (unsigned int) s_ptr->p, (unsigned int) data + length); return; } } in_uint32(s_ptr, len); if (s_ptr->p + len > data + length) { printf("Error in parsing licence key.\n"); printf("End of licence %x > supplied length (%x)\n", (unsigned int) s_ptr->p + len, (unsigned int) data + length); return; } home = getenv("HOME"); if (home == NULL) return; /* set and create the directory -- if it doesn't exist. */ fpath = xmalloc(strlen(home) + 11); STRNCPY(fpath, home, strlen(home) + 1); sprintf(fpath, "%s/.rdesktop", fpath); if (mkdir(fpath, 0700) == -1 && errno != EEXIST) { perror("mkdir"); exit(1); } /* set the real licence filename, and put a write lock on it. */ fname = xmalloc(strlen(fpath) + strlen(hostname) + 10); sprintf(fname, "%s/licence.%s", fpath, hostname); fnfd = open(fname, O_RDONLY); if (fnfd != -1) { fnfl.l_type = F_WRLCK; fnfl.l_whence = SEEK_SET; fnfl.l_start = 0; fnfl.l_len = 1; fcntl(fnfd, F_SETLK, &fnfl); } /* create a temporary licence file */ fnamewrk = xmalloc(strlen(fname) + 12); for (y = 0;; y++) { sprintf(fnamewrk, "%s.%lu", fname, (long unsigned int) y); fnwrkfd = open(fnamewrk, O_WRONLY | O_CREAT | O_EXCL, 0600); if (fnwrkfd == -1) { if (errno == EINTR || errno == EEXIST) continue; perror("create"); exit(1); } break; } /* write to the licence file */ for (y = 0; y < len;) { do { wlen = write(fnwrkfd, s_ptr->p + y, len - y); } while (wlen == -1 && errno == EINTR); if (wlen < 1) { perror("write"); unlink(fnamewrk); exit(1); } y += wlen; } /* close the file and rename it to fname */ if (close(fnwrkfd) == -1) { perror("close"); unlink(fnamewrk); exit(1); } if (rename(fnamewrk, fname) == -1) { perror("rename"); unlink(fnamewrk); exit(1); } /* close the file lock on fname */ if (fnfd != -1) { fnfl.l_type = F_UNLCK; fnfl.l_whence = SEEK_SET; fnfl.l_start = 0; fnfl.l_len = 1; fcntl(fnfd, F_SETLK, &fnfl); close(fnfd); } }
/* 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; }
/* Process data PDU */ static BOOL process_data_pdu(RDPCLIENT * This, STREAM s, uint32 * ext_disc_reason) { uint8 data_pdu_type; uint8 ctype; uint16 clen; uint32 len; uint32 roff, rlen; struct stream *ns = &(This->mppc_dict.ns); in_uint8s(s, 6); /* shareid, pad, streamid */ in_uint16(s, len); in_uint8(s, data_pdu_type); in_uint8(s, ctype); in_uint16(s, clen); clen -= 18; if (ctype & RDP_MPPC_COMPRESSED) { void * p; if (len > RDP_MPPC_DICT_SIZE) error("error decompressed packet size exceeds max\n"); if (mppc_expand(This, s->p, clen, ctype, &roff, &rlen) == -1) error("error while decompressing packet\n"); /* len -= 18; */ /* allocate memory and copy the uncompressed data into the temporary stream */ p = realloc(ns->data, rlen); if(p == NULL) { This->disconnect_reason = 262; return True; } ns->data = (uint8 *) p; memcpy((ns->data), (unsigned char *) (This->mppc_dict.hist + roff), rlen); ns->size = rlen; ns->end = (ns->data + ns->size); ns->p = ns->data; ns->rdp_hdr = ns->p; s = ns; } switch (data_pdu_type) { case RDP_DATA_PDU_UPDATE: process_update_pdu(This, s); break; case RDP_DATA_PDU_CONTROL: DEBUG(("Received Control PDU\n")); break; case RDP_DATA_PDU_SYNCHRONISE: DEBUG(("Received Sync PDU\n")); break; case RDP_DATA_PDU_POINTER: process_pointer_pdu(This, s); break; case RDP_DATA_PDU_BELL: ui_bell(This); break; case RDP_DATA_PDU_LOGON: DEBUG(("Received Logon PDU\n")); event_logon(This); /* User logged on */ break; case RDP_DATA_PDU_DISCONNECT: process_disconnect_pdu(s, ext_disc_reason); /* We used to return true and disconnect immediately here, but * Windows Vista sends a disconnect PDU with reason 0 when * reconnecting to a disconnected session, and MSTSC doesn't * drop the connection. I think we should just save the status. */ break; default: unimpl("data PDU %d\n", data_pdu_type); } 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; }