diff --git a/cssp.c b/cssp.c index 373ed25..c599791 100644 --- a/cssp.c +++ b/cssp.c @@ -139,13 +139,14 @@ cssp_gss_get_service_name(char *server, gss_name_t * name) } -static RD_BOOL -cssp_gss_wrap(gss_ctx_id_t ctx, STREAM in, STREAM out) +static STREAM +cssp_gss_wrap(gss_ctx_id_t ctx, STREAM in) { int conf_state; OM_uint32 major_status; OM_uint32 minor_status; gss_buffer_desc inbuf, outbuf; + STREAM out; inbuf.value = in->data; inbuf.length = s_length(in); @@ -157,35 +158,36 @@ cssp_gss_wrap(gss_ctx_id_t ctx, STREAM in, STREAM out) { cssp_gss_report_error(GSS_C_GSS_CODE, "Failed to encrypt and sign message", major_status, minor_status); - return False; + return NULL; } if (!conf_state) { logger(Core, Error, "cssp_gss_wrap(), GSS Confidentiality failed, no encryption of message performed."); - return False; + return NULL; } // write enc data to out stream - out->data = out->p = xmalloc(outbuf.length); - out->size = outbuf.length; + out = s_alloc(outbuf.length); out_uint8a(out, outbuf.value, outbuf.length); s_mark_end(out); + s_seek(out, 0); gss_release_buffer(&minor_status, &outbuf); - return True; + return out; } -static RD_BOOL -cssp_gss_unwrap(gss_ctx_id_t ctx, STREAM in, STREAM out) +static STREAM +cssp_gss_unwrap(gss_ctx_id_t ctx, STREAM in) { OM_uint32 major_status; OM_uint32 minor_status; gss_qop_t qop_state; gss_buffer_desc inbuf, outbuf; int conf_state; + STREAM out; inbuf.value = in->data; inbuf.length = s_length(in); @@ -196,17 +198,17 @@ cssp_gss_unwrap(gss_ctx_id_t ctx, STREAM in, STREAM out) { cssp_gss_report_error(GSS_C_GSS_CODE, "Failed to decrypt message", major_status, minor_status); - return False; + return NULL; } - out->data = out->p = xmalloc(outbuf.length); - out->size = outbuf.length; + out = s_alloc(outbuf.length); out_uint8a(out, outbuf.value, outbuf.length); s_mark_end(out); + s_seek(out, 0); gss_release_buffer(&minor_status, &outbuf); - return True; + return out; } @@ -585,10 +587,10 @@ cssp_send_tsrequest(STREAM token, STREAM auth, STREAM pubkey) } -RD_BOOL -cssp_read_tsrequest(STREAM token, STREAM pubkey) +STREAM +cssp_read_tsrequest(RD_BOOL pubkey) { - STREAM s; + STREAM s, out; int length; int tagval; struct stream packet; @@ -596,7 +598,7 @@ cssp_read_tsrequest(STREAM token, STREAM pubkey) s = tcp_recv(NULL, 4); if (s == NULL) - return False; + return NULL; // verify ASN.1 header if (s->p[0] != (BER_TAG_SEQUENCE | BER_TAG_CONSTRUCTED)) @@ -604,7 +606,7 @@ cssp_read_tsrequest(STREAM token, STREAM pubkey) logger(Protocol, Error, "cssp_read_tsrequest(), expected BER_TAG_SEQUENCE|BER_TAG_CONSTRUCTED, got %x", s->p[0]); - return False; + return NULL; } // peek at first 4 bytes to get full message length @@ -615,7 +617,7 @@ cssp_read_tsrequest(STREAM token, STREAM pubkey) else if (s->p[1] == 0x82) length = (s->p[2] << 8) | s->p[3]; else - return False; + return NULL; // receive the remainings of message s = tcp_recv(s, length); @@ -624,12 +626,12 @@ cssp_read_tsrequest(STREAM token, STREAM pubkey) // parse the response and into nego token if (!ber_in_header(s, &tagval, &length) || tagval != (BER_TAG_SEQUENCE | BER_TAG_CONSTRUCTED)) - return False; + return NULL; // version [0] if (!ber_in_header(s, &tagval, &length) || tagval != (BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 0)) - return False; + return NULL; if (!s_check_rem(s, length)) { @@ -639,23 +641,23 @@ cssp_read_tsrequest(STREAM token, STREAM pubkey) in_uint8s(s, length); // negoToken [1] - if (token) + if (!pubkey) { if (!ber_in_header(s, &tagval, &length) || tagval != (BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 1)) - return False; + return NULL; if (!ber_in_header(s, &tagval, &length) || tagval != (BER_TAG_SEQUENCE | BER_TAG_CONSTRUCTED)) - return False; + return NULL; if (!ber_in_header(s, &tagval, &length) || tagval != (BER_TAG_SEQUENCE | BER_TAG_CONSTRUCTED)) - return False; + return NULL; if (!ber_in_header(s, &tagval, &length) || tagval != (BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 0)) - return False; + return NULL; if (!ber_in_header(s, &tagval, &length) || tagval != BER_TAG_OCTET_STRING) - return False; + return NULL; if (!s_check_rem(s, length)) { @@ -663,29 +665,29 @@ cssp_read_tsrequest(STREAM token, STREAM pubkey) &packet); } - s_realloc(token, length); - s_reset(token); - out_uint8a(token, s->p, length); - s_mark_end(token); + out = s_alloc(length); + out_uint8a(out, s->p, length); + s_mark_end(out); + s_seek(out, 0); } - // pubKey [3] - if (pubkey) + else { if (!ber_in_header(s, &tagval, &length) || tagval != (BER_TAG_CTXT_SPECIFIC | BER_TAG_CONSTRUCTED | 3)) - return False; + return NULL; if (!ber_in_header(s, &tagval, &length) || tagval != BER_TAG_OCTET_STRING) - return False; + return NULL; - pubkey->data = pubkey->p = s->p; - pubkey->end = pubkey->data + length; - pubkey->size = length; + out = s_alloc(length); + out_uint8a(out, s->p, length); + s_mark_end(out); + s_seek(out, 0); } - return True; + return out; } RD_BOOL @@ -702,9 +704,14 @@ cssp_connect(char *server, char *user, char *domain, char *password, STREAM s) 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 }; + STREAM token; + STREAM pubkey, pubkey_cmp; + unsigned char *pubkey_data; + unsigned char *pubkey_cmp_data; + unsigned char first_byte; + + RD_BOOL ret; + STREAM blob; // Verify that system gss support spnego if (!cssp_gss_mech_available(desired_mech)) @@ -728,16 +735,19 @@ cssp_connect(char *server, char *user, char *domain, char *password, STREAM s) return False; } - tcp_tls_get_server_pubkey(&pubkey); + pubkey = tcp_tls_get_server_pubkey(); + if (pubkey == NULL) + return False; + pubkey_cmp = NULL; // 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; + token = NULL; input_tok.length = 0; output_tok.length = 0; minor_status = 0; @@ -758,6 +768,11 @@ cssp_connect(char *server, char *user, char *domain, char *password, STREAM s) &actual_mech, &output_tok, &actual_services, &actual_time); + // input_tok might have pointed to token's data, + // but it's safe to free it now after the call + s_free(token); + token = NULL; + if (GSS_ERROR(major_status)) { if (i == 0) @@ -782,39 +797,44 @@ cssp_connect(char *server, char *user, char *domain, char *password, STREAM s) // Send token to server if (output_tok.length != 0) { - if (output_tok.length > token.size) - s_realloc(&token, output_tok.length); - s_reset(&token); + token = s_alloc(output_tok.length); + out_uint8a(token, output_tok.value, output_tok.length); + s_mark_end(token); - out_uint8a(&token, output_tok.value, output_tok.length); - s_mark_end(&token); - - if (!cssp_send_tsrequest(&token, NULL, NULL)) - goto bail_out; + ret = cssp_send_tsrequest(token, NULL, NULL); + s_free(token); + token = NULL; (void) gss_release_buffer(&minor_status, &output_tok); + + if (!ret) + goto bail_out; } // 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)) + token = cssp_read_tsrequest(False); + if (token == NULL) goto bail_out; - input_tok.value = token.data; - input_tok.length = s_length(&token); + input_tok.length = s_length(token); + in_uint8p(token, input_tok.value, input_tok.length); } else { // Send encrypted pubkey for verification to server context_established = 1; - if (!cssp_gss_wrap(gss_ctx, &pubkey, &blob)) + blob = cssp_gss_wrap(gss_ctx, pubkey); + if (blob == NULL) goto bail_out; - if (!cssp_send_tsrequest(NULL, NULL, &blob)) + ret = cssp_send_tsrequest(NULL, NULL, blob); + + s_free(blob); + + if (!ret) goto bail_out; context_established = 1; @@ -825,37 +845,62 @@ cssp_connect(char *server, char *user, char *domain, char *password, STREAM s) } while (!context_established); + s_free(token); + // read tsrequest response and decrypt for public key validation - if (!cssp_read_tsrequest(NULL, &blob)) + blob = cssp_read_tsrequest(True); + if (blob == NULL) goto bail_out; - if (!cssp_gss_unwrap(gss_ctx, &blob, &pubkey_cmp)) + pubkey_cmp = cssp_gss_unwrap(gss_ctx, blob); + s_free(blob); + if (pubkey_cmp == NULL) goto bail_out; - pubkey_cmp.data[0] -= 1; + // the first byte gets 1 added before being sent by the server + // in order to protect against replays of the data sent earlier + // by the client + in_uint8(pubkey_cmp, first_byte); + s_seek(pubkey_cmp, 0); + out_uint8(pubkey_cmp, first_byte - 1); + s_seek(pubkey_cmp, 0); // validate public key - if (memcmp(pubkey.data, pubkey_cmp.data, s_length(&pubkey)) != 0) + in_uint8p(pubkey, pubkey_data, s_length(pubkey)); + in_uint8p(pubkey_cmp, pubkey_cmp_data, s_length(pubkey_cmp)); + if ((s_length(pubkey) != s_length(pubkey_cmp)) || + (memcmp(pubkey_data, pubkey_cmp_data, s_length(pubkey)) != 0)) { logger(Core, Error, "cssp_connect(), public key mismatch, cannot guarantee integrity of server connection"); goto bail_out; } + s_free(pubkey); + s_free(pubkey_cmp); + // Send TSCredentials ts_creds = cssp_encode_tscredentials(user, password, domain); - if (!cssp_gss_wrap(gss_ctx, ts_creds, &blob)) - goto bail_out; + blob = cssp_gss_wrap(gss_ctx, ts_creds); s_free(ts_creds); - if (!cssp_send_tsrequest(NULL, &blob, NULL)) + if (blob == NULL) + goto bail_out; + + ret = cssp_send_tsrequest(NULL, blob, NULL); + + s_free(blob); + + if (!ret) goto bail_out; return True; bail_out: - xfree(token.data); + s_free(token); + s_free(pubkey); + s_free(pubkey_cmp); return False; } diff --git a/proto.h b/proto.h index f7f0c6c..2ddb313 100644 --- a/proto.h +++ b/proto.h @@ -227,7 +227,7 @@ char *tcp_get_address(void); RD_BOOL tcp_is_connected(void); void tcp_reset_state(void); RD_BOOL tcp_tls_connect(void); -RD_BOOL tcp_tls_get_server_pubkey(STREAM s); +STREAM tcp_tls_get_server_pubkey(); void tcp_run_ui(RD_BOOL run); /* asn.c */ diff --git a/rdpsnd.c b/rdpsnd.c index b5b8ded..8bb2dbb 100644 --- a/rdpsnd.c +++ b/rdpsnd.c @@ -641,7 +641,7 @@ rdpsnd_queue_write(STREAM s, uint16 tick, uint8 index) queue_hi = next_hi; - packet->s = *s; + packet->s = s; packet->tick = tick; packet->index = index; @@ -675,7 +675,7 @@ rdpsnd_queue_clear(void) while (queue_pending != queue_hi) { packet = &packet_queue[queue_pending]; - xfree(packet->s.data); + s_free(packet->s); queue_pending = (queue_pending + 1) % MAX_QUEUE; } @@ -740,7 +740,7 @@ rdpsnd_queue_complete_pending(void) (packet->completion_tv.tv_usec - packet->arrive_tv.tv_usec); elapsed /= 1000; - xfree(packet->s.data); + s_free(packet->s); rdpsnd_send_waveconfirm((packet->tick + elapsed) % 65536, packet->index); queue_pending = (queue_pending + 1) % MAX_QUEUE; } diff --git a/rdpsnd.h b/rdpsnd.h index 81e87c4..b31e175 100644 --- a/rdpsnd.h +++ b/rdpsnd.h @@ -19,7 +19,7 @@ struct audio_packet { - struct stream s; + STREAM s; uint16 tick; uint8 index; diff --git a/rdpsnd_alsa.c b/rdpsnd_alsa.c index 26e5cc8..25fbc31 100644 --- a/rdpsnd_alsa.c +++ b/rdpsnd_alsa.c @@ -376,7 +376,7 @@ alsa_play(void) return; packet = rdpsnd_queue_current_packet(); - out = &packet->s; + out = packet->s; next_tick = rdpsnd_queue_next_tick(); diff --git a/rdpsnd_dsp.c b/rdpsnd_dsp.c index 9661e09..8cc10fb 100644 --- a/rdpsnd_dsp.c +++ b/rdpsnd_dsp.c @@ -175,8 +175,8 @@ rdpsnd_dsp_resample_supported(RD_WAVEFORMATEX * format) return True; } -uint32 -rdpsnd_dsp_resample(unsigned char **out, unsigned char *in, unsigned int size, +STREAM +rdpsnd_dsp_resample(unsigned char *in, unsigned int size, RD_WAVEFORMATEX * format, RD_BOOL stream_be) { UNUSED(stream_be); @@ -190,13 +190,15 @@ rdpsnd_dsp_resample(unsigned char **out, unsigned char *in, unsigned int size, int innum, outnum; unsigned char *tmpdata = NULL, *tmp = NULL; int samplewidth = format->wBitsPerSample / 8; + STREAM out; int outsize = 0; + unsigned char *data; int i; if ((resample_to_bitspersample == format->wBitsPerSample) && (resample_to_channels == format->nChannels) && (resample_to_srate == format->nSamplesPerSec)) - return 0; + return NULL; #ifdef B_ENDIAN if (!stream_be) @@ -260,7 +262,7 @@ rdpsnd_dsp_resample(unsigned char **out, unsigned char *in, unsigned int size, { logger(Sound, Warning, "rdpsndp_dsp_resample_set(), no sample rate converter available"); - return 0; + return NULL; } outnum = ((float) innum * ((float) resample_to_srate / (float) format->nSamplesPerSec)) + 1; @@ -285,8 +287,9 @@ rdpsnd_dsp_resample(unsigned char **out, unsigned char *in, unsigned int size, xfree(infloat); outsize = resample_data.output_frames_gen * resample_to_channels * samplewidth; - *out = (unsigned char *) xmalloc(outsize); - src_float_to_short_array(outfloat, (short *) *out, + out = s_alloc(outsize); + out_uint8p(out, data, outsize); + src_float_to_short_array(outfloat, (short *) data, resample_data.output_frames_gen * resample_to_channels); xfree(outfloat); @@ -302,8 +305,9 @@ rdpsnd_dsp_resample(unsigned char **out, unsigned char *in, unsigned int size, outnum = (innum * ratio1k) / 1000; outsize = outnum * samplewidth; - *out = (unsigned char *) xmalloc(outsize); - bzero(*out, outsize); + out = s_alloc(outsize); + out_uint8p(out, data, outsize); + bzero(data, outsize); for (i = 0; i < outsize / (resample_to_channels * samplewidth); i++) { @@ -331,7 +335,7 @@ rdpsnd_dsp_resample(unsigned char **out, unsigned char *in, unsigned int size, cval1 += (sint8) (cval2 * part) / 100; - memcpy(*out + (i * resample_to_channels * samplewidth) + + memcpy(data + (i * resample_to_channels * samplewidth) + (samplewidth * j), &cval1, samplewidth); } } @@ -349,14 +353,14 @@ rdpsnd_dsp_resample(unsigned char **out, unsigned char *in, unsigned int size, sval1 += (sint16) (sval2 * part) / 100; - memcpy(*out + (i * resample_to_channels * samplewidth) + + memcpy(data + (i * resample_to_channels * samplewidth) + (samplewidth * j), &sval1, samplewidth); } } #else /* Nearest neighbor search */ for (j = 0; j < resample_to_channels; j++) { - memcpy(*out + (i * resample_to_channels * samplewidth) + (samplewidth * j), + memcpy(out + (i * resample_to_channels * samplewidth) + (samplewidth * j), in + (source * resample_to_channels * samplewidth) + (samplewidth * j), samplewidth); } @@ -378,7 +382,7 @@ rdpsnd_dsp_resample(unsigned char **out, unsigned char *in, unsigned int size, { for (i = 0; i < outsize; i++) { - *out[i] = *out[i * 2]; + data[i] = data[i * 2]; } outsize /= 2; } @@ -386,16 +390,17 @@ rdpsnd_dsp_resample(unsigned char **out, unsigned char *in, unsigned int size, #ifdef B_ENDIAN if (!stream_be) - rdpsnd_dsp_swapbytes(*out, outsize, format); + rdpsnd_dsp_swapbytes(data, outsize, format); #endif - return outsize; + + return out; } STREAM rdpsnd_dsp_process(unsigned char *data, unsigned int size, struct audio_driver * current_driver, RD_WAVEFORMATEX * format) { - static struct stream out; + STREAM out; RD_BOOL stream_be = False; /* softvol and byteswap do not change the amount of data they @@ -411,20 +416,19 @@ rdpsnd_dsp_process(unsigned char *data, unsigned int size, struct audio_driver * } #endif - out.data = NULL; + out = NULL; if (current_driver->need_resampling) - out.size = rdpsnd_dsp_resample(&out.data, data, size, format, stream_be); + out = rdpsnd_dsp_resample(data, size, format, stream_be); - if (out.data == NULL) + if (out == NULL) { - out.data = (unsigned char *) xmalloc(size); - memcpy(out.data, data, size); - out.size = size; + out = s_alloc(size); + out_uint8a(out, data, size); } - out.p = out.data; - out.end = out.p + out.size; + s_mark_end(out); + s_seek(out, 0); - return &out; + return out; } diff --git a/rdpsnd_libao.c b/rdpsnd_libao.c index 6d1996f..3a59788 100644 --- a/rdpsnd_libao.c +++ b/rdpsnd_libao.c @@ -167,7 +167,7 @@ libao_play(void) return; packet = rdpsnd_queue_current_packet(); - out = &packet->s; + out = packet->s; next_tick = rdpsnd_queue_next_tick(); diff --git a/rdpsnd_oss.c b/rdpsnd_oss.c index ae5df21..0705236 100644 --- a/rdpsnd_oss.c +++ b/rdpsnd_oss.c @@ -410,7 +410,7 @@ oss_play(void) return; packet = rdpsnd_queue_current_packet(); - out = &packet->s; + out = packet->s; len = s_remaining(out); diff --git a/rdpsnd_pulse.c b/rdpsnd_pulse.c index 9bcee39..a763541 100644 --- a/rdpsnd_pulse.c +++ b/rdpsnd_pulse.c @@ -1154,7 +1154,7 @@ pulse_play(void) do { packet = rdpsnd_queue_current_packet(); - out = &packet->s; + out = packet->s; ti = pa_stream_get_timing_info(playback_stream); if (ti == NULL) diff --git a/rdpsnd_sgi.c b/rdpsnd_sgi.c index d94f648..d62f12c 100644 --- a/rdpsnd_sgi.c +++ b/rdpsnd_sgi.c @@ -254,7 +254,7 @@ sgi_play(void) return; packet = rdpsnd_queue_current_packet(); - out = (STREAM) (void *) &(packet->s); + out = packet->s; len = s_remaining(out); diff --git a/stream.c b/stream.c index 8400446..a4b9542 100644 --- a/stream.c +++ b/stream.c @@ -37,6 +37,19 @@ s_alloc(unsigned int size) return s; } +STREAM +s_inherit(unsigned char *data, unsigned int size) +{ + STREAM s; + + s = xmalloc(sizeof(struct stream)); + memset(s, 0, sizeof(struct stream)); + s->p = s->data = data; + s->size = size; + + return s; +} + void s_realloc(STREAM s, unsigned int size) { @@ -71,6 +84,8 @@ s_reset(STREAM s) void s_free(STREAM s) { + if (s == NULL) + return; free(s->data); free(s); } diff --git a/stream.h b/stream.h index b9e16dc..5e7464b 100644 --- a/stream.h +++ b/stream.h @@ -43,6 +43,8 @@ typedef struct stream /* Return a newly allocated STREAM object of the specified size */ STREAM s_alloc(unsigned int size); +/* Wrap an existing buffer in a STREAM object, transferring ownership */ +STREAM s_inherit(unsigned char *data, unsigned int size); /* Resize an existing STREAM object, keeping all data and offsets intact */ void s_realloc(STREAM s, unsigned int size); /* Free STREAM object and its associated buffer */ @@ -60,6 +62,10 @@ size_t in_ansi_string(STREAM s, char *string, size_t len); #define s_push_layer(s,h,n) { (s)->h = (s)->p; (s)->p += n; } #define s_pop_layer(s,h) (s)->p = (s)->h; #define s_mark_end(s) (s)->end = (s)->p; +/* Return current read offset in the STREAM */ +#define s_tell(s) (size_t)((s)->p - (s)->data) +/* Set current read offset in the STREAM */ +#define s_seek(s,o) (s)->p = (s)->data; s_assert_r(s,o); (s)->p += o; /* Returns number of bytes that can still be read from STREAM */ #define s_remaining(s) (size_t)((s)->end - (s)->p) #define s_check_rem(s,n) (((s)->p <= (s)->end) && ((size_t)n <= s_remaining(s))) diff --git a/tcp.c b/tcp.c index 726f6c0..8634631 100644 --- a/tcp.c +++ b/tcp.c @@ -399,8 +399,8 @@ fail: } /* Get public key from server of TLS 1.x connection */ -RD_BOOL -tcp_tls_get_server_pubkey(STREAM s) +STREAM +tcp_tls_get_server_pubkey() { int ret; unsigned int list_size; @@ -413,8 +413,7 @@ tcp_tls_get_server_pubkey(STREAM s) int pk_size; uint8_t pk_data[1024]; - s->data = s->p = NULL; - s->size = 0; + STREAM s = NULL; cert_list = gnutls_certificate_get_peers(g_tls_session, &list_size); @@ -466,11 +465,10 @@ tcp_tls_get_server_pubkey(STREAM s) goto out; } - s->size = pk_size; - s->data = s->p = xmalloc(s->size); - memcpy((void *)s->data, (void *)pk_data, pk_size); - s->p = s->data; - s->end = s->p + s->size; + s = s_alloc(pk_size); + out_uint8a(s, pk_data, pk_size); + s_mark_end(s); + s_seek(s, 0); out: if ((e.size != 0) && (e.data)) { @@ -481,7 +479,7 @@ out: free(m.data); } - return (s->size != 0); + return s; } /* Helper function to determine if rdesktop should resolve hostnames again or not */