From 4411d3d8073eda45726b6328ef47cb28facfb9a7 Mon Sep 17 00:00:00 2001 From: Olga Kornievskaia Date: Thu, 27 Jan 2011 13:52:08 -0500 Subject: [PATCH] first stab at integrity and privacy note: privacy will not work when we have more than 1 outstanding rpcs which generates out of order replies which sspi does not allow when privacy is enabled. adding auth_wrap() and auth_unwrap() to per-message gss token protection required adding these methods to auth_sys and auth_non. linux server doesnt support v2 kerberos tokens that have rotated data. sspi will always produce such tokens for aes. thus thus code was only tested for v1 kerberos tokens (ie des). --- libtirpc/src/auth_none.c | 8 +++ libtirpc/src/auth_sspi.c | 105 +++++++++++++++++++++++++++++++-- libtirpc/src/auth_unix.c | 8 +++ libtirpc/src/authsspi_prot.c | 33 ++++++----- libtirpc/src/clnt_vc.c | 8 ++- libtirpc/tirpc/rpc/auth.h | 10 ++-- libtirpc/tirpc/rpc/auth_sspi.h | 5 ++ 7 files changed, 149 insertions(+), 28 deletions(-) diff --git a/libtirpc/src/auth_none.c b/libtirpc/src/auth_none.c index fc5654e..5bfa48a 100644 --- a/libtirpc/src/auth_none.c +++ b/libtirpc/src/auth_none.c @@ -156,6 +156,12 @@ authnone_destroy(AUTH *client) { } +static bool_t +authnone_wrap(AUTH *auth, XDR *xdrs, xdrproc_t func, caddr_t args, u_int seq) +{ + return ((*func)(xdrs, args)); +} + static struct auth_ops * authnone_ops() { @@ -171,6 +177,8 @@ authnone_ops() ops.ah_validate = authnone_validate; ops.ah_refresh = authnone_refresh; ops.ah_destroy = authnone_destroy; + ops.ah_wrap = authnone_wrap; + ops.ah_unwrap = authnone_wrap; } mutex_unlock(&ops_lock); return (&ops); diff --git a/libtirpc/src/auth_sspi.c b/libtirpc/src/auth_sspi.c index d618199..507d5a6 100644 --- a/libtirpc/src/auth_sspi.c +++ b/libtirpc/src/auth_sspi.c @@ -324,7 +324,7 @@ authsspi_validate(AUTH *auth, struct opaque_auth *verf, u_int seq) #else maj_stat = sspi_verify_mic(&gd->ctx, cur_seq, &signbuf, &checksum, &qop_state); #endif - if (maj_stat != SEC_E_OK || qop_state != gd->sec->qop) { + if (maj_stat != SEC_E_OK) { log_debug("authsspi_validate: VerifySignature failed with %x", maj_stat); if (maj_stat == SEC_E_NO_AUTHENTICATING_AUTHORITY) { gd->established = FALSE; @@ -373,7 +373,10 @@ authsspi_refresh(AUTH *auth, void *tmp) print_rpc_gss_sec(gd->sec); - for (i=0;;i++) { + if (gd->sec->svc == RPCSEC_SSPI_SVC_PRIVACY) + flags |= ISC_REQ_CONFIDENTIALITY; + + for (i=0;;i++) { /* print the token we just received */ if (recv_tokenp != SSPI_C_NO_BUFFER) { log_debug("The token we just received (length %d):", @@ -498,7 +501,7 @@ authsspi_refresh(AUTH *auth, void *tmp) #else maj_stat = sspi_verify_mic(&gd->ctx, 0, &bufin, &gd->gc_wire_verf, &qop_state); #endif - if (maj_stat != SEC_E_OK || qop_state != gd->sec->qop) { + if (maj_stat != SEC_E_OK) { log_debug("authgss_refresh: sspi_verify_mic failed with %x", maj_stat); if (maj_stat == SEC_E_NO_AUTHENTICATING_AUTHORITY) { gd->established = FALSE; @@ -623,7 +626,7 @@ authsspi_wrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr) } bool_t -authsspi_unwrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr) +authsspi_unwrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr, u_int seq) { struct rpc_sspi_data *gd; @@ -636,7 +639,7 @@ authsspi_unwrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr) } return (xdr_rpc_sspi_data(xdrs, xdr_func, xdr_ptr, &gd->ctx, gd->sec->qop, - gd->sec->svc, gd->gc.gc_seq)); + gd->sec->svc, seq)); } uint32_t sspi_get_mic(PCtxtHandle ctx, u_int qop, u_int seq, @@ -720,6 +723,98 @@ uint32_t sspi_import_name(sspi_buffer_desc *name_in, sspi_name_t *name_out) return SEC_E_OK; } + +uint32_t sspi_wrap(PCtxtHandle ctx, u_int seq, sspi_buffer_desc *bufin, + sspi_buffer_desc *bufout, u_int *conf_state) +{ + uint32_t maj_stat; + SecBufferDesc BuffDesc; + SecBuffer SecBuff[3]; + ULONG ulQop = 0; + SecPkgContext_Sizes ContextSizes; + PBYTE p; + + maj_stat = QueryContextAttributes(ctx, SECPKG_ATTR_SIZES, + &ContextSizes); + if (maj_stat != SEC_E_OK) + goto out; + + BuffDesc.ulVersion = 0; + BuffDesc.cBuffers = 3; + BuffDesc.pBuffers = SecBuff; + + SecBuff[0].cbBuffer = ContextSizes.cbSecurityTrailer; + SecBuff[0].BufferType = SECBUFFER_TOKEN; + SecBuff[0].pvBuffer = malloc(ContextSizes.cbSecurityTrailer); + + SecBuff[1].cbBuffer = bufin->length; + SecBuff[1].BufferType = SECBUFFER_DATA; + SecBuff[1].pvBuffer = bufin->value; + log_hexdump(0, "plaintext:", bufin->value, bufin->length, 0); + + SecBuff[2].cbBuffer = ContextSizes.cbBlockSize; + SecBuff[2].BufferType = SECBUFFER_PADDING; + SecBuff[2].pvBuffer = malloc(ContextSizes.cbBlockSize); + + maj_stat = EncryptMessage(ctx, ulQop, &BuffDesc, 0); + if (maj_stat != SEC_E_OK) + goto out_free; + + bufout->length = SecBuff[0].cbBuffer + SecBuff[1].cbBuffer + SecBuff[2].cbBuffer; + p = bufout->value = malloc(bufout->length); + memcpy(p, SecBuff[0].pvBuffer, SecBuff[0].cbBuffer); + p += SecBuff[0].cbBuffer; + memcpy(p, SecBuff[1].pvBuffer, SecBuff[1].cbBuffer); + p += SecBuff[1].cbBuffer; + memcpy(p, SecBuff[2].pvBuffer, SecBuff[2].cbBuffer); +out_free: + free(SecBuff[0].pvBuffer); + free(SecBuff[2].pvBuffer); + + if (!maj_stat) + log_hexdump(0, "cipher:", bufout->value, bufout->length, 0); +out: + return maj_stat; +} + +uint32_t sspi_upwrap(PCtxtHandle ctx, sspi_buffer_desc *bufin, + sspi_buffer_desc *bufout, u_int *conf_state, + u_int *qop_state) +{ + uint32_t maj_stat; + SecBufferDesc BuffDesc; + SecBuffer SecBuff[2]; + ULONG ulQop = 0; + + BuffDesc.ulVersion = 0; + BuffDesc.cBuffers = 2; + BuffDesc.pBuffers = SecBuff; + + SecBuff[0].cbBuffer = bufin->length; + SecBuff[0].BufferType = SECBUFFER_STREAM; + SecBuff[0].pvBuffer = bufin->value; + + SecBuff[1].cbBuffer = 0; + SecBuff[1].BufferType = SECBUFFER_DATA; + SecBuff[1].pvBuffer = NULL; + + log_hexdump(0, "cipher:", bufin->value, bufin->length, 0); + + maj_stat = DecryptMessage(ctx, &BuffDesc, 0, &ulQop); + if (maj_stat != SEC_E_OK) return maj_stat; + + bufout->length = SecBuff[1].cbBuffer; + bufout->value = malloc(bufout->length); + memcpy(bufout->value, SecBuff[1].pvBuffer, bufout->length); + + log_hexdump(0, "data:", bufout->value, bufout->length, 0); + + *conf_state = 1; + *qop_state = 0; + + return SEC_E_OK; +} + /* useful as i add more mechanisms */ #define DEBUG #ifdef DEBUG diff --git a/libtirpc/src/auth_unix.c b/libtirpc/src/auth_unix.c index c3a69ad..84507b3 100644 --- a/libtirpc/src/auth_unix.c +++ b/libtirpc/src/auth_unix.c @@ -353,6 +353,12 @@ marshal_new_auth(auth) XDR_DESTROY(xdrs); } +static bool_t +authunix_wrap(AUTH *auth, XDR *xdrs, xdrproc_t func, caddr_t args, u_int seq) +{ + return ((*func)(xdrs, args)); +} + static struct auth_ops * authunix_ops() { @@ -368,6 +374,8 @@ authunix_ops() ops.ah_validate = authunix_validate; ops.ah_refresh = authunix_refresh; ops.ah_destroy = authunix_destroy; + ops.ah_wrap = authunix_wrap; + ops.ah_unwrap = authunix_wrap; } mutex_unlock(&ops_lock); return (&ops); diff --git a/libtirpc/src/authsspi_prot.c b/libtirpc/src/authsspi_prot.c index 09c0709..5539faa 100644 --- a/libtirpc/src/authsspi_prot.c +++ b/libtirpc/src/authsspi_prot.c @@ -108,7 +108,7 @@ xdr_rpc_sspi_wrap_data(XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr, /* Skip databody length. */ start = XDR_GETPOS(xdrs); - //XDR_SETPOS(xdrs, start + 4); + XDR_SETPOS(xdrs, start + 4); /* Marshal rpc_gss_data_t (sequence number + arguments). */ if (!xdr_u_int(xdrs, &seq) || !(*xdr_func)(xdrs, xdr_ptr)) @@ -117,15 +117,14 @@ xdr_rpc_sspi_wrap_data(XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr, /* Set databuf to marshalled rpc_gss_data_t. */ databuf.length = end - start - 4; - //XDR_SETPOS(xdrs, start + 4); - //databuf.value = XDR_INLINE(xdrs, databuf.length); - databuf.value = xdrrec_getoutbase(xdrs) + 1; + XDR_SETPOS(xdrs, start + 4); + databuf.value = XDR_INLINE(xdrs, databuf.length); xdr_stat = FALSE; if (svc == RPCSEC_SSPI_SVC_INTEGRITY) { /* Marshal databody_integ length. */ - //XDR_SETPOS(xdrs, start); + XDR_SETPOS(xdrs, start); if (!xdr_u_int(xdrs, (u_int *)&databuf.length)) return (FALSE); @@ -141,9 +140,9 @@ xdr_rpc_sspi_wrap_data(XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr, return (FALSE); } /* Marshal checksum. */ - //XDR_SETPOS(xdrs, end); + XDR_SETPOS(xdrs, end); xdr_stat = xdr_bytes(xdrs, (char **)&wrapbuf.value, - (u_int *)&wrapbuf.length, MAX_NETOBJ_SZ); + (u_int *)&wrapbuf.length, (u_int)-1); #if 0 gss_release_buffer(&min_stat, &wrapbuf); #else @@ -155,6 +154,8 @@ xdr_rpc_sspi_wrap_data(XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr, #if 0 maj_stat = gss_wrap(&min_stat, ctx, TRUE, qop, &databuf, &conf_state, &wrapbuf); +#else + maj_stat = sspi_wrap(ctx, 0, &databuf, &wrapbuf, &conf_state); #endif if (maj_stat != SEC_E_OK) { log_debug("xdr_rpc_sspi_wrap_data: sspi_wrap failed with %x", maj_stat); @@ -163,7 +164,7 @@ xdr_rpc_sspi_wrap_data(XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr, /* Marshal databody_priv. */ XDR_SETPOS(xdrs, start); xdr_stat = xdr_bytes(xdrs, (char **)&wrapbuf.value, - (u_int *)&wrapbuf.length, MAX_NETOBJ_SZ); + (u_int *)&wrapbuf.length, (u_int)-1); #if 0 gss_release_buffer(&min_stat, &wrapbuf); #else @@ -196,7 +197,7 @@ xdr_rpc_sspi_unwrap_data(XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr, if (svc == RPCSEC_SSPI_SVC_INTEGRITY) { /* Decode databody_integ. */ if (!xdr_bytes(xdrs, (char **)&databuf.value, (u_int *)&databuf.length, - MAX_NETOBJ_SZ)) { + (u_int)-1)) { log_debug("xdr_rpc_sspi_unwrap_data: xdr decode databody_integ failed"); return (FALSE); } @@ -224,7 +225,7 @@ xdr_rpc_sspi_unwrap_data(XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr, sspi_release_buffer(&wrapbuf); #endif - if (maj_stat != SEC_E_OK || qop_state != qop) { + if (maj_stat != SEC_E_OK) { #if 0 gss_release_buffer(&min_stat, &databuf); #else @@ -238,7 +239,7 @@ xdr_rpc_sspi_unwrap_data(XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr, else if (svc == RPCSEC_SSPI_SVC_PRIVACY) { /* Decode databody_priv. */ if (!xdr_bytes(xdrs, (char **)&wrapbuf.value, (u_int *)&wrapbuf.length, - MAX_NETOBJ_SZ)) { + (u_int)-1)) { log_debug("xdr_rpc_sspi_unwrap_data: xdr decode databody_priv failed"); return (FALSE); } @@ -246,6 +247,8 @@ xdr_rpc_sspi_unwrap_data(XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr, #if 0 maj_stat = gss_unwrap(&min_stat, ctx, &wrapbuf, &databuf, &conf_state, &qop_state); +#else + maj_stat = sspi_upwrap(ctx, &wrapbuf, &databuf, &conf_state, &qop_state); #endif #if 0 gss_release_buffer(&min_stat, &wrapbuf); @@ -253,8 +256,7 @@ xdr_rpc_sspi_unwrap_data(XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr, sspi_release_buffer(&wrapbuf); #endif /* Verify encryption and QOP. */ - if (maj_stat != SEC_E_OK || qop_state != qop || - conf_state != TRUE) { + if (maj_stat != SEC_E_OK) { #if 0 gss_release_buffer(&min_stat, &databuf); #else @@ -272,11 +274,12 @@ xdr_rpc_sspi_unwrap_data(XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr, #if 0 gss_release_buffer(&min_stat, &databuf); #else - sspi_release_buffer(&databuf); + sspi_release_buffer(&databuf); #endif /* Verify sequence number. */ if (xdr_stat == TRUE && seq_num != seq) { - log_debug("wrong sequence number in databody"); + log_debug("wrong sequence number in databody received %d expected %d", + seq_num, seq); return (FALSE); } diff --git a/libtirpc/src/clnt_vc.c b/libtirpc/src/clnt_vc.c index 9ae30d6..5849a41 100644 --- a/libtirpc/src/clnt_vc.c +++ b/libtirpc/src/clnt_vc.c @@ -523,13 +523,14 @@ call_again: if ((! XDR_PUTBYTES(xdrs, ct->ct_u.ct_mcallc, ct->ct_mpos)) || (! XDR_PUTINT32(xdrs, (int32_t *)&proc)) || (! AUTH_MARSHALL(cl->cl_auth, xdrs, &seq)) || - (! (*xdr_args)(xdrs, args_ptr))) { + (! AUTH_WRAP(cl->cl_auth, xdrs, xdr_args, args_ptr))) { if (ct->ct_error.re_status == RPC_SUCCESS) ct->ct_error.re_status = RPC_CANTENCODEARGS; (void)xdrrec_endofrecord(xdrs, TRUE); release_fd_lock(ct->ct_fd, mask); return (ct->ct_error.re_status); } + if (! xdrrec_endofrecord(xdrs, shipnow)) { release_fd_lock(ct->ct_fd, mask); ct->ct_error.re_status = RPC_CANTSEND; @@ -552,7 +553,7 @@ call_again: /* * Keep receiving until we get a valid transaction id */ - + while (TRUE) { mutex_lock(&clnt_fd_lock); while ((vc_fd_locks[WINSOCK_HANDLE_HASH(ct->ct_fd)] && @@ -613,7 +614,8 @@ call_again: &ct->reply_msg.acpted_rply.ar_verf, seq)) { ct->ct_error.re_status = RPC_AUTHERROR; ct->ct_error.re_why = AUTH_INVALIDRESP; - } else if (! (*xdr_results)(xdrs, results_ptr)) { + } + else if (! AUTH_UNWRAP(cl->cl_auth, xdrs, xdr_results, results_ptr, seq)) { if (ct->ct_error.re_status == RPC_SUCCESS) ct->ct_error.re_status = RPC_CANTDECODERES; } diff --git a/libtirpc/tirpc/rpc/auth.h b/libtirpc/tirpc/rpc/auth.h index 82e0e33..d37c263 100644 --- a/libtirpc/tirpc/rpc/auth.h +++ b/libtirpc/tirpc/rpc/auth.h @@ -198,7 +198,7 @@ typedef struct __auth { /* encode data for wire */ int (*ah_wrap) (struct __auth *, XDR *, xdrproc_t, caddr_t); /* decode data for wire */ - int (*ah_unwrap) (struct __auth *, XDR *, xdrproc_t, caddr_t); + int (*ah_unwrap) (struct __auth *, XDR *, xdrproc_t, caddr_t, u_int); } *ah_ops; void *ah_private; @@ -245,12 +245,12 @@ typedef struct __auth { ((*((auth)->ah_ops->ah_wrap))(auth, xdrs, \ xfunc, xwhere)) -#define AUTH_UNWRAP(auth, xdrs, xfunc, xwhere) \ +#define AUTH_UNWRAP(auth, xdrs, xfunc, xwhere, seq) \ ((*((auth)->ah_ops->ah_unwrap))(auth, xdrs, \ - xfunc, xwhere)) -#define auth_unwrap(auth, xdrs, xfunc, xwhere) \ + xfunc, xwhere, seq)) +#define auth_unwrap(auth, xdrs, xfunc, xwhere, seq) \ ((*((auth)->ah_ops->ah_unwrap))(auth, xdrs, \ - xfunc, xwhere)) + xfunc, xwhere, seq)) __BEGIN_DECLS diff --git a/libtirpc/tirpc/rpc/auth_sspi.h b/libtirpc/tirpc/rpc/auth_sspi.h index 5ebe4d8..53f06b6 100644 --- a/libtirpc/tirpc/rpc/auth_sspi.h +++ b/libtirpc/tirpc/rpc/auth_sspi.h @@ -105,6 +105,11 @@ uint32_t sspi_get_mic(void *ctx, u_int qop, u_int seq, sspi_buffer_desc *bufin, sspi_buffer_desc *bufout); uint32_t sspi_verify_mic(void *ctx, u_int seq, sspi_buffer_desc *bufin, sspi_buffer_desc *bufout, u_int *qop_state); +uint32_t sspi_wrap(void *ctx, u_int seq, sspi_buffer_desc *bufin, + sspi_buffer_desc *bufout, u_int *conf_state); +uint32_t sspi_unwrap(void *ctx, sspi_buffer_desc *bufin, + sspi_buffer_desc *bufout, u_int *conf_state, + u_int *qop_state); void sspi_release_buffer(sspi_buffer_desc *buf); uint32_t sspi_import_name(sspi_buffer_desc *name_in, sspi_name_t *name_out);