aboutsummaryrefslogtreecommitdiff
path: root/xdico/gsaslstr.c
diff options
context:
space:
mode:
Diffstat (limited to 'xdico/gsaslstr.c')
-rw-r--r--xdico/gsaslstr.c297
1 files changed, 297 insertions, 0 deletions
diff --git a/xdico/gsaslstr.c b/xdico/gsaslstr.c
new file mode 100644
index 0000000..83fdc8e
--- /dev/null
+++ b/xdico/gsaslstr.c
@@ -0,0 +1,297 @@
+/* This file is part of GNU Dico.
+ Copyright (C) 2008-2019 Sergey Poznyakoff
+
+ GNU Dico 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, or (at your option)
+ any later version.
+
+ GNU Dico 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 GNU Dico. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <dico.h>
+#include <libi18n.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <gsasl.h>
+
+#ifdef WITH_GSASL
+
+#define _ERR_SASL 0x1
+#define _ERR_TRANSPORT 0x2
+
+struct g_buf {
+ char *buffer;
+ size_t pos;
+ size_t size;
+ size_t level;
+};
+
+#define g_buf_ptr(p) ((p).buffer + (p).pos)
+#define g_buf_level(p) ((p).level - (p).pos)
+#define g_buf_drop(p) (p).level = (p).pos = 0
+#define g_buf_free(p) free((p).buffer)
+#define g_buf_advance(p, s) \
+ do { \
+ if (((p).pos += s) >= (p).level) \
+ g_buf_drop(p); \
+ } while (0)
+
+int
+g_buf_grow(struct g_buf *pb, const char *ptr, size_t size)
+{
+ if (!pb->buffer) {
+ pb->buffer = malloc(size);
+ pb->size = size;
+ pb->level = 0;
+ } else if (pb->size - pb->level < size) {
+ size_t newsize = pb->size + size;
+ pb->buffer = realloc(pb->buffer, newsize);
+ if (pb->buffer)
+ pb->size = newsize;
+ }
+
+ if (!pb->buffer)
+ return 1;
+
+ memcpy(pb->buffer + pb->level, ptr, size);
+ pb->level += size;
+ return 0;
+}
+
+struct _gsasl_str {
+ Gsasl_session *sess; /* Session context */
+ int last_err; /* Last GSASL error code */
+ int errflag;
+ dico_stream_t transport;
+ struct g_buf buf;
+};
+
+#define set_error(s, flag, code) \
+ do { \
+ (s)->last_err = code; \
+ (s)->errflag = flag; \
+ } while (0)
+
+static int
+_gsasl_read(void *data, char *buf, size_t size, size_t *pret)
+{
+ struct _gsasl_str *s = data;
+ int rc;
+ size_t len;
+ char *bufp = NULL;
+
+ if ((len = g_buf_level(s->buf))) {
+ if (len > size)
+ len = size;
+ memcpy(buf, g_buf_ptr(s->buf), len);
+ g_buf_advance(s->buf, len);
+ if (pret)
+ *pret = len;
+ return 0;
+ }
+
+ do {
+ char tmp[80];
+ size_t sz;
+ int status;
+
+ status = dico_stream_read(s->transport, tmp, sizeof(tmp), &sz);
+ if (status == EINTR)
+ continue;
+ else if (status) {
+ free (bufp);
+ return status;
+ }
+ rc = g_buf_grow(&s->buf, tmp, sz);
+ if (rc)
+ return rc;
+
+ rc = gsasl_decode(s->sess,
+ g_buf_ptr(s->buf),
+ g_buf_level(s->buf),
+ &bufp, &len);
+ } while (rc == GSASL_NEEDS_MORE);
+
+ if (rc != GSASL_OK) {
+ set_error(s, _ERR_SASL, rc);
+ free(bufp);
+ errno = EIO;
+ return 1;
+ }
+
+ g_buf_drop(s->buf);
+ if (len > size) {
+ memcpy(buf, bufp, size);
+ g_buf_grow(&s->buf, bufp + size, len - size);
+ len = size;
+ } else {
+ memcpy(buf, bufp, len);
+ }
+
+ if (pret)
+ *pret = len;
+
+ free(bufp);
+ return 0;
+}
+
+static int
+_gsasl_write(void *data, const char *buf, size_t size, size_t *pret)
+{
+ struct _gsasl_str *s = data;
+ char *obuf = NULL;
+ size_t olen = 0;
+
+ int rc = gsasl_encode(s->sess, buf, size, &obuf, &olen);
+ if (rc != GSASL_OK) {
+ set_error(s, _ERR_SASL, rc);
+ errno = EIO;
+ return 1;
+ }
+ rc = dico_stream_write(s->transport, obuf, olen);
+ free(obuf);
+ if (rc) {
+ set_error(s, _ERR_TRANSPORT, errno);
+ return 1;
+ }
+ if (pret)
+ *pret = olen;
+ return 0;
+}
+
+static int
+_gsasl_destroy(void *data)
+{
+ struct _gsasl_str *s = data;
+ g_buf_free(s->buf);
+ dico_stream_destroy(&s->transport);
+ return 0;
+}
+
+/*
+ FIXME:
+static const char *
+_gsasl_error_string(void *data, int code)
+{
+
+}
+*/
+
+static int
+_gsasl_flush(void *data)
+{
+ struct _gsasl_str *s = data;
+ return dico_stream_flush(s->transport);
+}
+
+static int
+_gsasl_close(void *data)
+{
+ struct _gsasl_str *s = data;
+ return dico_stream_close(s->transport);
+}
+
+static int
+_gsasl_ioctl(void *data, int code, void *call_data)
+{
+ struct _gsasl_str *s = data;
+
+ switch (code) {
+ case DICO_IOCTL_GET_TRANSPORT:
+ *(dico_stream_t*)call_data = s->transport;
+ break;
+
+ case DICO_IOCTL_SET_TRANSPORT:
+ s->transport = call_data;
+ break;
+
+ case DICO_IOCTL_BYTES_IN:
+ *(off_t*)call_data = dico_stream_bytes_in(s->transport);
+ break;
+
+ case DICO_IOCTL_BYTES_OUT:
+ *(off_t*)call_data = dico_stream_bytes_out(s->transport);
+ break;
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+dico_stream_t
+dico_gsasl_stream(Gsasl_session *sess, dico_stream_t transport)
+{
+ int rc;
+ struct _gsasl_str *s;
+ dico_stream_t str;
+
+ s = malloc(sizeof(*s));
+ if (!s)
+ return NULL;
+ memset(s, 0, sizeof(*s));
+ rc = dico_stream_create(&str, DICO_STREAM_READ|DICO_STREAM_WRITE, s);
+ if (rc) {
+ free(s);
+ return NULL;
+ }
+ s->sess = sess;
+ s->transport = transport;
+ dico_stream_set_write(str, _gsasl_write);
+ dico_stream_set_read(str, _gsasl_read);
+ dico_stream_set_flush(str, _gsasl_flush);
+ dico_stream_set_close(str, _gsasl_close);
+ dico_stream_set_destroy(str, _gsasl_destroy);
+ dico_stream_set_ioctl(str, _gsasl_ioctl);
+ dico_stream_set_buffer(str, dico_buffer_line, 1024);
+ return str;
+}
+
+int
+insert_gsasl_stream(Gsasl_session *sess, dico_stream_t *pstr)
+{
+ int rc;
+ dico_stream_t gsasl_str;
+ dico_stream_t str = *pstr;
+ dico_stream_t prev = NULL, s;
+
+ while ((rc = dico_stream_ioctl(str, DICO_IOCTL_GET_TRANSPORT, &s)) == 0
+ && s) {
+ prev = str;
+ str = s;
+ }
+ if (rc && errno != EINVAL && errno != ENOSYS) {
+ dico_log(L_ERR, errno, _("Cannot get transport stream"));
+ return rc;
+ }
+ if (!str) {
+ dico_log(L_ERR, 0, _("Cannot get transport stream"));
+ return 1;
+ }
+
+ gsasl_str = dico_gsasl_stream(sess, str);
+ if (!gsasl_str) {
+ dico_log(L_ERR, errno, _("Cannot create GSASL stream"));
+ return 1;
+ }
+
+ if (!prev)
+ *pstr = gsasl_str;
+ else if (dico_stream_ioctl(prev, DICO_IOCTL_SET_TRANSPORT, gsasl_str)) {
+ dico_log(L_ERR, errno, _("Cannot install GSASL stream"));
+ return 1;
+ }
+ return 0;
+}
+
+#endif

Return to:

Send suggestions and report system problems to the System administrator.