1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-24 21:34:56 +03:00
samba-mirror/source3/libads/sasl_wrapping.c
Alexander Bokovoy 2dbaade13a libads: abstract out SASL wrapping code
Prepare for rebasing libads on top of libsmbldap.

To make libads using 'struct smbldap_state' instead of direct LDAP
structure, we need to abstract out libads logic from connection
handling. SASL wrapping does not really depend on availability of LDAP
handle and does not need direct access to ADS_STRUCT. As result, we'll
be able to move SASL wrapping code under smbldap once the latter is able
to pass settings that libads passes to the SASL wrapping.

Signed-off-by: Alexander Bokovoy <ab@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
2017-05-17 23:02:09 +02:00

352 lines
8.2 KiB
C

/*
Unix SMB/CIFS implementation.
ads sasl wrapping code
Copyright (C) Stefan Metzmacher 2007
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
#include "ads.h"
void ndr_print_ads_saslwrap_struct(struct ndr_print *ndr, const char *name, const struct ads_saslwrap *r)
{
ndr_print_struct(ndr, name, "saslwrap");
ndr->depth++;
ndr_print_uint16(ndr, "wrap_type", r->wrap_type);
#ifdef HAVE_LDAP_SASL_WRAPPING
ndr_print_ptr(ndr, "sbiod", r->sbiod);
#endif /* HAVE_LDAP_SASL_WRAPPING */
ndr_print_ptr(ndr, "mem_ctx", r->mem_ctx);
ndr_print_ptr(ndr, "wrap_ops", r->wrap_ops);
ndr_print_ptr(ndr, "wrap_private_data", r->wrap_private_data);
ndr_print_struct(ndr, name, "in");
ndr->depth++;
ndr_print_uint32(ndr, "ofs", r->in.ofs);
ndr_print_uint32(ndr, "needed", r->in.needed);
ndr_print_uint32(ndr, "left", r->in.left);
ndr_print_uint32(ndr, "max_wrapped", r->in.max_wrapped);
ndr_print_uint32(ndr, "min_wrapped", r->in.min_wrapped);
ndr_print_uint32(ndr, "size", r->in.size);
ndr_print_array_uint8(ndr, "buf", r->in.buf, r->in.size);
ndr->depth--;
ndr_print_struct(ndr, name, "out");
ndr->depth++;
ndr_print_uint32(ndr, "ofs", r->out.ofs);
ndr_print_uint32(ndr, "left", r->out.left);
ndr_print_uint32(ndr, "max_unwrapped", r->out.max_unwrapped);
ndr_print_uint32(ndr, "sig_size", r->out.sig_size);
ndr_print_uint32(ndr, "size", r->out.size);
ndr_print_array_uint8(ndr, "buf", r->out.buf, r->out.size);
ndr->depth--;
}
#ifdef HAVE_LDAP_SASL_WRAPPING
static int ads_saslwrap_setup(Sockbuf_IO_Desc *sbiod, void *arg)
{
struct ads_saslwrap *wrap = (struct ads_saslwrap *)arg;
wrap->sbiod = sbiod;
sbiod->sbiod_pvt = wrap;
return 0;
}
static int ads_saslwrap_remove(Sockbuf_IO_Desc *sbiod)
{
return 0;
}
static ber_slen_t ads_saslwrap_prepare_inbuf(struct ads_saslwrap *wrap)
{
wrap->in.ofs = 0;
wrap->in.needed = 0;
wrap->in.left = 0;
wrap->in.size = 4 + wrap->in.min_wrapped;
wrap->in.buf = talloc_array(wrap->mem_ctx,
uint8_t, wrap->in.size);
if (!wrap->in.buf) {
return -1;
}
return 0;
}
static ber_slen_t ads_saslwrap_grow_inbuf(struct ads_saslwrap *wrap)
{
if (wrap->in.size == (4 + wrap->in.needed)) {
return 0;
}
wrap->in.size = 4 + wrap->in.needed;
wrap->in.buf = talloc_realloc(wrap->mem_ctx,
wrap->in.buf,
uint8_t, wrap->in.size);
if (!wrap->in.buf) {
return -1;
}
return 0;
}
static void ads_saslwrap_shrink_inbuf(struct ads_saslwrap *wrap)
{
talloc_free(wrap->in.buf);
wrap->in.buf = NULL;
wrap->in.size = 0;
wrap->in.ofs = 0;
wrap->in.needed = 0;
wrap->in.left = 0;
}
static ber_slen_t ads_saslwrap_read(Sockbuf_IO_Desc *sbiod,
void *buf, ber_len_t len)
{
struct ads_saslwrap *wrap =
(struct ads_saslwrap *)sbiod->sbiod_pvt;
ber_slen_t ret;
/* If ofs < 4 it means we don't have read the length header yet */
if (wrap->in.ofs < 4) {
ret = ads_saslwrap_prepare_inbuf(wrap);
if (ret < 0) return ret;
ret = LBER_SBIOD_READ_NEXT(sbiod,
wrap->in.buf + wrap->in.ofs,
4 - wrap->in.ofs);
if (ret <= 0) return ret;
wrap->in.ofs += ret;
if (wrap->in.ofs < 4) goto eagain;
wrap->in.needed = RIVAL(wrap->in.buf, 0);
if (wrap->in.needed > wrap->in.max_wrapped) {
errno = EINVAL;
return -1;
}
if (wrap->in.needed < wrap->in.min_wrapped) {
errno = EINVAL;
return -1;
}
ret = ads_saslwrap_grow_inbuf(wrap);
if (ret < 0) return ret;
}
/*
* if there's more data needed from the remote end,
* we need to read more
*/
if (wrap->in.needed > 0) {
ret = LBER_SBIOD_READ_NEXT(sbiod,
wrap->in.buf + wrap->in.ofs,
wrap->in.needed);
if (ret <= 0) return ret;
wrap->in.ofs += ret;
wrap->in.needed -= ret;
if (wrap->in.needed > 0) goto eagain;
}
/*
* if we have a complete packet and have not yet unwrapped it
* we need to call the mech specific unwrap() hook
*/
if (wrap->in.needed == 0 && wrap->in.left == 0) {
ADS_STATUS status;
status = wrap->wrap_ops->unwrap(wrap);
if (!ADS_ERR_OK(status)) {
errno = EACCES;
return -1;
}
}
/*
* if we have unwrapped data give it to the caller
*/
if (wrap->in.left > 0) {
ret = MIN(wrap->in.left, len);
memcpy(buf, wrap->in.buf + wrap->in.ofs, ret);
wrap->in.ofs += ret;
wrap->in.left -= ret;
/*
* if no more is left shrink the inbuf,
* this will trigger reading a new SASL packet
* from the remote stream in the next call
*/
if (wrap->in.left == 0) {
ads_saslwrap_shrink_inbuf(wrap);
}
return ret;
}
/*
* if we don't have anything for the caller yet,
* tell him to ask again
*/
eagain:
errno = EAGAIN;
return -1;
}
static ber_slen_t ads_saslwrap_prepare_outbuf(struct ads_saslwrap *wrap,
uint32_t len)
{
wrap->out.ofs = 0;
wrap->out.left = 0;
wrap->out.size = 4 + wrap->out.sig_size + len;
wrap->out.buf = talloc_array(wrap->mem_ctx,
uint8_t, wrap->out.size);
if (!wrap->out.buf) {
return -1;
}
return 0;
}
static void ads_saslwrap_shrink_outbuf(struct ads_saslwrap *wrap)
{
talloc_free(wrap->out.buf);
wrap->out.buf = NULL;
wrap->out.size = 0;
wrap->out.ofs = 0;
wrap->out.left = 0;
}
static ber_slen_t ads_saslwrap_write(Sockbuf_IO_Desc *sbiod,
void *buf, ber_len_t len)
{
struct ads_saslwrap *wrap =
(struct ads_saslwrap *)sbiod->sbiod_pvt;
ber_slen_t ret, rlen;
/* if the buffer is empty, we need to wrap in incoming buffer */
if (wrap->out.left == 0) {
ADS_STATUS status;
if (len == 0) {
errno = EINVAL;
return -1;
}
rlen = MIN(len, wrap->out.max_unwrapped);
ret = ads_saslwrap_prepare_outbuf(wrap, rlen);
if (ret < 0) return ret;
status = wrap->wrap_ops->wrap(wrap, (uint8_t *)buf, rlen);
if (!ADS_ERR_OK(status)) {
errno = EACCES;
return -1;
}
RSIVAL(wrap->out.buf, 0, wrap->out.left - 4);
} else {
rlen = -1;
}
ret = LBER_SBIOD_WRITE_NEXT(sbiod,
wrap->out.buf + wrap->out.ofs,
wrap->out.left);
if (ret <= 0) return ret;
wrap->out.ofs += ret;
wrap->out.left -= ret;
if (wrap->out.left == 0) {
ads_saslwrap_shrink_outbuf(wrap);
}
if (rlen > 0) return rlen;
errno = EAGAIN;
return -1;
}
static int ads_saslwrap_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg)
{
struct ads_saslwrap *wrap =
(struct ads_saslwrap *)sbiod->sbiod_pvt;
int ret;
switch (opt) {
case LBER_SB_OPT_DATA_READY:
if (wrap->in.left > 0) {
return 1;
}
ret = LBER_SBIOD_CTRL_NEXT(sbiod, opt, arg);
break;
default:
ret = LBER_SBIOD_CTRL_NEXT(sbiod, opt, arg);
break;
}
return ret;
}
static int ads_saslwrap_close(Sockbuf_IO_Desc *sbiod)
{
return 0;
}
static const Sockbuf_IO ads_saslwrap_sockbuf_io = {
ads_saslwrap_setup, /* sbi_setup */
ads_saslwrap_remove, /* sbi_remove */
ads_saslwrap_ctrl, /* sbi_ctrl */
ads_saslwrap_read, /* sbi_read */
ads_saslwrap_write, /* sbi_write */
ads_saslwrap_close /* sbi_close */
};
ADS_STATUS ads_setup_sasl_wrapping(struct ads_saslwrap *wrap, LDAP *ld,
const struct ads_saslwrap_ops *ops,
void *private_data)
{
ADS_STATUS status;
Sockbuf *sb;
Sockbuf_IO *io = discard_const_p(Sockbuf_IO, &ads_saslwrap_sockbuf_io);
int rc;
rc = ldap_get_option(ld, LDAP_OPT_SOCKBUF, &sb);
status = ADS_ERROR_LDAP(rc);
if (!ADS_ERR_OK(status)) {
return status;
}
/* setup the real wrapping callbacks */
rc = ber_sockbuf_add_io(sb, io, LBER_SBIOD_LEVEL_TRANSPORT, wrap);
status = ADS_ERROR_LDAP(rc);
if (!ADS_ERR_OK(status)) {
return status;
}
wrap->wrap_ops = ops;
wrap->wrap_private_data = private_data;
return ADS_SUCCESS;
}
#else
ADS_STATUS ads_setup_sasl_wrapping(struct ads_saslwrap *wrap, LDAP *ld,
const struct ads_saslwrap_ops *ops,
void *private_data)
{
return ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
}
#endif /* HAVE_LDAP_SASL_WRAPPING */