if OPENCONNECT_WIN32
openconnect_SOURCES += openconnect.rc
endif
-library_srcs = ssl.c http.c http-auth.c auth-common.c auth-html.c library.c compat.c lzs.c mainloop.c script.c ntlm.c digest.c mtucalc.c openconnect-internal.h
+library_srcs = ssl.c http.c textbuf.c http-auth.c auth-common.c auth-html.c library.c compat.c lzs.c mainloop.c script.c ntlm.c digest.c mtucalc.c openconnect-internal.h
lib_srcs_cisco = auth.c cstp.c
lib_srcs_juniper = oncp.c lzo.c auth-juniper.c
lib_srcs_pulse = pulse.c
#include "openconnect-internal.h"
-/* Ick. Yet another wheel to reinvent. But although we could pull it
- in from OpenSSL, we can't from GnuTLS */
-
-static inline int b64_char(char c)
-{
- if (c >= 'A' && c <= 'Z')
- return c - 'A';
- if (c >= 'a' && c <= 'z')
- return c - 'a' + 26;
- if (c >= '0' && c <= '9')
- return c - '0' + 52;
- if (c == '+')
- return 62;
- if (c == '/')
- return 63;
- return -1;
-}
-
-void *openconnect_base64_decode(int *ret_len, const char *in)
-{
- unsigned char *buf;
- int b[4];
- int len = strlen(in);
-
- if (len & 3) {
- *ret_len = -EINVAL;
- return NULL;
- }
- len = (len * 3) / 4;
- buf = malloc(len);
- if (!buf) {
- *ret_len = -ENOMEM;
- return NULL;
- }
-
- len = 0;
- while (*in) {
- if (!in[1] || !in[2] || !in[3])
- goto err;
- b[0] = b64_char(in[0]);
- b[1] = b64_char(in[1]);
- if (b[0] < 0 || b[1] < 0)
- goto err;
- buf[len++] = (b[0] << 2) | (b[1] >> 4);
-
- if (in[2] == '=') {
- if (in[3] != '=' || in[4] != 0)
- goto err;
- break;
- }
- b[2] = b64_char(in[2]);
- if (b[2] < 0)
- goto err;
- buf[len++] = (b[1] << 4) | (b[2] >> 2);
- if (in[3] == '=') {
- if (in[4] != 0)
- goto err;
- break;
- }
- b[3] = b64_char(in[3]);
- if (b[3] < 0)
- goto err;
- buf[len++] = (b[2] << 6) | b[3];
- in += 4;
- }
- *ret_len = len;
- return buf;
-
- err:
- free(buf);
- *ret_len = -EINVAL;
- return NULL;
-}
-
-static const char b64_table[] = {
- 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
- 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
- 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
- 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
-};
-
-void buf_append_base64(struct oc_text_buf *buf, const void *bytes, int len,
- int line_len)
-{
- const unsigned char *in = bytes;
- int hibits;
-
- if (!buf || buf->error)
- return;
-
- unsigned int needed = ((len + 2u) / 3) * 4 + 1;
- if (line_len)
- needed += needed / line_len;
-
- if (needed >= (unsigned)(INT_MAX - buf->pos)) {
- buf->error = -E2BIG;
- return;
- }
-
- if (buf_ensure_space(buf, needed))
- return;
-
- int ll = 0;
- while (len > 0) {
- if (line_len) {
- ll += 4;
- if (ll >= line_len) {
- ll = 0;
- buf->data[buf->pos++] = '\n';
- }
- }
-
- buf->data[buf->pos++] = b64_table[in[0] >> 2];
- hibits = (in[0] << 4) & 0x30;
- if (len == 1) {
- buf->data[buf->pos++] = b64_table[hibits];
- buf->data[buf->pos++] = '=';
- buf->data[buf->pos++] = '=';
- break;
- }
- buf->data[buf->pos++] = b64_table[hibits | (in[1] >> 4)];
- hibits = (in[1] << 2) & 0x3c;
- if (len == 2) {
- buf->data[buf->pos++] = b64_table[hibits];
- buf->data[buf->pos++] = '=';
- break;
- }
- buf->data[buf->pos++] = b64_table[hibits | (in[2] >> 6)];
- buf->data[buf->pos++] = b64_table[in[2] & 0x3f];
- in += 3;
- len -= 3;
- }
- buf->data[buf->pos] = 0;
-}
-
static int basic_authorization(struct openconnect_info *vpninfo, int proxy,
struct http_auth_state *auth_state,
struct oc_text_buf *hdrbuf)
static int proxy_write(struct openconnect_info *vpninfo, char *buf, size_t len);
static int proxy_read(struct openconnect_info *vpninfo, char *buf, size_t len);
-#define BUF_CHUNK_SIZE 4096
-
-struct oc_text_buf *buf_alloc(void)
-{
- return calloc(1, sizeof(struct oc_text_buf));
-}
-
-void buf_append_urlencoded(struct oc_text_buf *buf, const char *str)
-{
- while (str && *str) {
- unsigned char c = *str;
- if (c < 0x80 && (isalnum((int)(c)) || c=='-' || c=='_' || c=='.' || c=='~'))
- buf_append_bytes(buf, str, 1);
- else
- buf_append(buf, "%%%02x", c);
-
- str++;
- }
-}
-
-void buf_append_xmlescaped(struct oc_text_buf *buf, const char *str)
-{
- while (str && *str) {
- unsigned char c = *str;
- if (c=='<' || c=='>' || c=='&' || c=='"' || c=='\'')
- buf_append(buf, "&#x%02x;", c);
- else
- buf_append_bytes(buf, str, 1);
-
- str++;
- }
-}
-
-void buf_append_be16(struct oc_text_buf *buf, uint16_t val)
-{
- unsigned char b[2];
-
- store_be16(b, val);
-
- buf_append_bytes(buf, b, 2);
-}
-
-void buf_append_be32(struct oc_text_buf *buf, uint32_t val)
-{
- unsigned char b[4];
-
- store_be32(b, val);
-
- buf_append_bytes(buf, b, 4);
-}
-
-void buf_append_le16(struct oc_text_buf *buf, uint16_t val)
-{
- unsigned char b[2];
-
- store_le16(b, val);
-
- buf_append_bytes(buf, b, 2);
-}
-
-void buf_append_hex(struct oc_text_buf *buf, const void *str, unsigned len)
-{
- const unsigned char *data = str;
- unsigned i;
-
- for (i = 0; i < len; i++)
- buf_append(buf, "%02x", (unsigned)data[i]);
-}
-
-void buf_truncate(struct oc_text_buf *buf)
-{
- if (!buf)
- return;
-
- if (buf->data)
- memset(buf->data, 0, buf->pos);
-
- buf->pos = 0;
-}
-
-int buf_ensure_space(struct oc_text_buf *buf, int len)
-{
- unsigned int new_buf_len;
-
- if (!buf)
- return -ENOMEM;
-
- new_buf_len = (buf->pos + len + BUF_CHUNK_SIZE - 1) & ~(BUF_CHUNK_SIZE - 1);
-
- if (new_buf_len <= buf->buf_len)
- return 0;
-
- if (new_buf_len > INT_MAX) {
- buf->error = -E2BIG;
- return buf->error;
- } else {
- realloc_inplace(buf->data, new_buf_len);
- if (!buf->data)
- buf->error = -ENOMEM;
- else
- buf->buf_len = new_buf_len;
- }
- return buf->error;
-}
-
-void __attribute__ ((format (printf, 2, 3)))
- buf_append(struct oc_text_buf *buf, const char *fmt, ...)
-{
- va_list ap;
-
- if (!buf || buf->error)
- return;
-
- if (buf_ensure_space(buf, 1))
- return;
-
- while (1) {
- int max_len = buf->buf_len - buf->pos, ret;
-
- va_start(ap, fmt);
- ret = vsnprintf(buf->data + buf->pos, max_len, fmt, ap);
- va_end(ap);
- if (ret < 0) {
- buf->error = -EIO;
- break;
- } else if (ret < max_len) {
- buf->pos += ret;
- break;
- } else if (buf_ensure_space(buf, ret))
- break;
- }
-}
-
-void buf_append_bytes(struct oc_text_buf *buf, const void *bytes, int len)
-{
- if (!buf || buf->error)
- return;
-
- if (buf_ensure_space(buf, len + 1))
- return;
-
- memcpy(buf->data + buf->pos, bytes, len);
- buf->pos += len;
- buf->data[buf->pos] = 0;
-}
-
-void buf_append_from_utf16le(struct oc_text_buf *buf, const void *_utf16)
-{
- const unsigned char *utf16 = _utf16;
- unsigned char utf8[4];
- int c;
-
- if (!utf16)
- return;
-
- while (utf16[0] || utf16[1]) {
- if ((utf16[1] & 0xfc) == 0xd8 && (utf16[3] & 0xfc) == 0xdc) {
- c = ((load_le16(utf16) & 0x3ff) << 10)|
- (load_le16(utf16 + 2) & 0x3ff);
- c += 0x10000;
- utf16 += 4;
- } else {
- c = load_le16(utf16);
- utf16 += 2;
- }
-
- if (c < 0x80) {
- utf8[0] = c;
- buf_append_bytes(buf, utf8, 1);
- } else if (c < 0x800) {
- utf8[0] = 0xc0 | (c >> 6);
- utf8[1] = 0x80 | (c & 0x3f);
- buf_append_bytes(buf, utf8, 2);
- } else if (c < 0x10000) {
- utf8[0] = 0xe0 | (c >> 12);
- utf8[1] = 0x80 | ((c >> 6) & 0x3f);
- utf8[2] = 0x80 | (c & 0x3f);
- buf_append_bytes(buf, utf8, 3);
- } else {
- utf8[0] = 0xf0 | (c >> 18);
- utf8[1] = 0x80 | ((c >> 12) & 0x3f);
- utf8[2] = 0x80 | ((c >> 6) & 0x3f);
- utf8[3] = 0x80 | (c & 0x3f);
- buf_append_bytes(buf, utf8, 4);
- }
- }
- utf8[0] = 0;
- buf_append_bytes(buf, utf8, 1);
-}
-
-int get_utf8char(const char **p)
-{
- const char *utf8 = *p;
- unsigned char c;
- int utfchar, nr_extra, min;
-
- c = *(utf8++);
- if (c < 128) {
- utfchar = c;
- nr_extra = 0;
- min = 0;
- } else if ((c & 0xe0) == 0xc0) {
- utfchar = c & 0x1f;
- nr_extra = 1;
- min = 0x80;
- } else if ((c & 0xf0) == 0xe0) {
- utfchar = c & 0x0f;
- nr_extra = 2;
- min = 0x800;
- } else if ((c & 0xf8) == 0xf0) {
- utfchar = c & 0x07;
- nr_extra = 3;
- min = 0x10000;
- } else {
- return -EILSEQ;
- }
-
- while (nr_extra--) {
- c = *(utf8++);
- if ((c & 0xc0) != 0x80)
- return -EILSEQ;
-
- utfchar <<= 6;
- utfchar |= (c & 0x3f);
- }
- if (utfchar > 0x10ffff || utfchar < min)
- return -EILSEQ;
-
- *p = utf8;
- return utfchar;
-}
-
-int buf_append_utf16le(struct oc_text_buf *buf, const char *utf8)
-{
- int utfchar, len = 0;
-
- /* Ick. Now I'm implementing my own UTF8 handling too. Perhaps it's
- time to bite the bullet and start requiring something like glib? */
- while (*utf8) {
- utfchar = get_utf8char(&utf8);
- if (utfchar < 0) {
- if (buf)
- buf->error = utfchar;
- return utfchar;
- }
- if (!buf)
- continue;
-
- if (utfchar >= 0x10000) {
- utfchar -= 0x10000;
- if (buf_ensure_space(buf, 4))
- return buf_error(buf);
- store_le16(buf->data + buf->pos, (utfchar >> 10) | 0xd800);
- store_le16(buf->data + buf->pos + 2, (utfchar & 0x3ff) | 0xdc00);
- buf->pos += 4;
- len += 4;
- } else {
- if (buf_ensure_space(buf, 2))
- return buf_error(buf);
- store_le16(buf->data + buf->pos, utfchar);
- buf->pos += 2;
- len += 2;
- }
- }
-
- /* We were only being used for validation */
- if (!buf)
- return 0;
-
- /* Ensure UTF16 is NUL-terminated */
- if (buf_ensure_space(buf, 2))
- return buf_error(buf);
- buf->data[buf->pos] = buf->data[buf->pos + 1] = 0;
-
- return len;
-}
-
-int buf_error(struct oc_text_buf *buf)
-{
- return buf ? buf->error : -ENOMEM;
-}
-
-int buf_free(struct oc_text_buf *buf)
-{
- int error = buf_error(buf);
-
- if (buf) {
- buf_truncate(buf);
- if (buf->data)
- free(buf->data);
- free(buf);
- }
-
- return error;
-}
-
/*
* We didn't really want to have to do this for ourselves -- one might have
* thought that it would be available in a library somewhere. But neither
struct oc_auth_form *form,
struct oc_form_opt *opt);
-/* http.c */
+/* textbuf,c */
struct oc_text_buf *buf_alloc(void);
-void dump_buf(struct openconnect_info *vpninfo, char prefix, char *buf);
-void dump_buf_hex(struct openconnect_info *vpninfo, int loglevel, char prefix, unsigned char *buf, int len);
+int buf_error(struct oc_text_buf *buf);
+int buf_free(struct oc_text_buf *buf);
+void buf_truncate(struct oc_text_buf *buf);
int buf_ensure_space(struct oc_text_buf *buf, int len);
+void buf_append_bytes(struct oc_text_buf *buf, const void *bytes, int len);
void __attribute__ ((format (printf, 2, 3)))
buf_append(struct oc_text_buf *buf, const char *fmt, ...);
-void buf_append_bytes(struct oc_text_buf *buf, const void *bytes, int len);
+void buf_append_urlencoded(struct oc_text_buf *buf, const char *str);
+void buf_append_xmlescaped(struct oc_text_buf *buf, const char *str);
void buf_append_be16(struct oc_text_buf *buf, uint16_t val);
void buf_append_be32(struct oc_text_buf *buf, uint32_t val);
void buf_append_le16(struct oc_text_buf *buf, uint16_t val);
int buf_append_utf16le(struct oc_text_buf *buf, const char *utf8);
int get_utf8char(const char **utf8);
void buf_append_from_utf16le(struct oc_text_buf *buf, const void *utf16);
-void buf_truncate(struct oc_text_buf *buf);
-void buf_append_urlencoded(struct oc_text_buf *buf, const char *str);
-void buf_append_xmlescaped(struct oc_text_buf *buf, const char *str);
-int buf_error(struct oc_text_buf *buf);
-int buf_free(struct oc_text_buf *buf);
+void buf_append_base64(struct oc_text_buf *buf, const void *bytes, int len, int line_len);
+
+/* http.c */
+void dump_buf(struct openconnect_info *vpninfo, char prefix, char *buf);
+void dump_buf_hex(struct openconnect_info *vpninfo, int loglevel, char prefix, unsigned char *buf, int len);
char *openconnect_create_useragent(const char *base);
int process_proxy(struct openconnect_info *vpninfo, int ssl_sock);
int internal_parse_url(const char *url, char **res_proto, char **res_host,
void http_common_headers(struct openconnect_info *vpninfo, struct oc_text_buf *buf);
/* http-auth.c */
-void buf_append_base64(struct oc_text_buf *buf, const void *bytes, int len, int line_len);
void *openconnect_base64_decode(int *len, const char *in);
void clear_auth_states(struct openconnect_info *vpninfo,
struct http_auth_state *auth_states, int reset);
--- /dev/null
+/*
+ * OpenConnect (SSL + DTLS) VPN client
+ *
+ * Copyright © 2008-2016 Intel Corporation.
+ * Copyright © 2016-2021 David Woodhouse.
+ *
+ * Author: David Woodhouse <dwmw2@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "openconnect-internal.h"
+
+#define BUF_CHUNK_SIZE 4096
+
+struct oc_text_buf *buf_alloc(void)
+{
+ return calloc(1, sizeof(struct oc_text_buf));
+}
+
+int buf_error(struct oc_text_buf *buf)
+{
+ return buf ? buf->error : -ENOMEM;
+}
+
+int buf_free(struct oc_text_buf *buf)
+{
+ int error = buf_error(buf);
+
+ if (buf) {
+ buf_truncate(buf);
+ if (buf->data)
+ free(buf->data);
+ free(buf);
+ }
+
+ return error;
+}
+
+void buf_truncate(struct oc_text_buf *buf)
+{
+ if (!buf)
+ return;
+
+ if (buf->data)
+ memset(buf->data, 0, buf->pos);
+
+ buf->pos = 0;
+}
+
+int buf_ensure_space(struct oc_text_buf *buf, int len)
+{
+ unsigned int new_buf_len;
+
+ if (!buf)
+ return -ENOMEM;
+
+ new_buf_len = (buf->pos + len + BUF_CHUNK_SIZE - 1) & ~(BUF_CHUNK_SIZE - 1);
+
+ if (new_buf_len <= buf->buf_len)
+ return 0;
+
+ if (new_buf_len > INT_MAX) {
+ buf->error = -E2BIG;
+ return buf->error;
+ } else {
+ realloc_inplace(buf->data, new_buf_len);
+ if (!buf->data)
+ buf->error = -ENOMEM;
+ else
+ buf->buf_len = new_buf_len;
+ }
+ return buf->error;
+}
+
+void buf_append_bytes(struct oc_text_buf *buf, const void *bytes, int len)
+{
+ if (!buf || buf->error)
+ return;
+
+ if (buf_ensure_space(buf, len + 1))
+ return;
+
+ memcpy(buf->data + buf->pos, bytes, len);
+ buf->pos += len;
+ buf->data[buf->pos] = 0;
+}
+
+void __attribute__ ((format (printf, 2, 3)))
+ buf_append(struct oc_text_buf *buf, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!buf || buf->error)
+ return;
+
+ if (buf_ensure_space(buf, 1))
+ return;
+
+ while (1) {
+ int max_len = buf->buf_len - buf->pos, ret;
+
+ va_start(ap, fmt);
+ ret = vsnprintf(buf->data + buf->pos, max_len, fmt, ap);
+ va_end(ap);
+ if (ret < 0) {
+ buf->error = -EIO;
+ break;
+ } else if (ret < max_len) {
+ buf->pos += ret;
+ break;
+ } else if (buf_ensure_space(buf, ret))
+ break;
+ }
+}
+
+void buf_append_urlencoded(struct oc_text_buf *buf, const char *str)
+{
+ while (str && *str) {
+ unsigned char c = *str;
+ if (c < 0x80 && (isalnum((int)(c)) || c=='-' || c=='_' || c=='.' || c=='~'))
+ buf_append_bytes(buf, str, 1);
+ else
+ buf_append(buf, "%%%02x", c);
+
+ str++;
+ }
+}
+
+void buf_append_xmlescaped(struct oc_text_buf *buf, const char *str)
+{
+ while (str && *str) {
+ unsigned char c = *str;
+ if (c=='<' || c=='>' || c=='&' || c=='"' || c=='\'')
+ buf_append(buf, "&#x%02x;", c);
+ else
+ buf_append_bytes(buf, str, 1);
+
+ str++;
+ }
+}
+
+void buf_append_be16(struct oc_text_buf *buf, uint16_t val)
+{
+ unsigned char b[2];
+
+ store_be16(b, val);
+
+ buf_append_bytes(buf, b, 2);
+}
+
+void buf_append_be32(struct oc_text_buf *buf, uint32_t val)
+{
+ unsigned char b[4];
+
+ store_be32(b, val);
+
+ buf_append_bytes(buf, b, 4);
+}
+
+void buf_append_le16(struct oc_text_buf *buf, uint16_t val)
+{
+ unsigned char b[2];
+
+ store_le16(b, val);
+
+ buf_append_bytes(buf, b, 2);
+}
+
+void buf_append_hex(struct oc_text_buf *buf, const void *str, unsigned len)
+{
+ const unsigned char *data = str;
+ unsigned i;
+
+ for (i = 0; i < len; i++)
+ buf_append(buf, "%02x", (unsigned)data[i]);
+}
+
+void buf_append_from_utf16le(struct oc_text_buf *buf, const void *_utf16)
+{
+ const unsigned char *utf16 = _utf16;
+ unsigned char utf8[4];
+ int c;
+
+ if (!utf16)
+ return;
+
+ while (utf16[0] || utf16[1]) {
+ if ((utf16[1] & 0xfc) == 0xd8 && (utf16[3] & 0xfc) == 0xdc) {
+ c = ((load_le16(utf16) & 0x3ff) << 10)|
+ (load_le16(utf16 + 2) & 0x3ff);
+ c += 0x10000;
+ utf16 += 4;
+ } else {
+ c = load_le16(utf16);
+ utf16 += 2;
+ }
+
+ if (c < 0x80) {
+ utf8[0] = c;
+ buf_append_bytes(buf, utf8, 1);
+ } else if (c < 0x800) {
+ utf8[0] = 0xc0 | (c >> 6);
+ utf8[1] = 0x80 | (c & 0x3f);
+ buf_append_bytes(buf, utf8, 2);
+ } else if (c < 0x10000) {
+ utf8[0] = 0xe0 | (c >> 12);
+ utf8[1] = 0x80 | ((c >> 6) & 0x3f);
+ utf8[2] = 0x80 | (c & 0x3f);
+ buf_append_bytes(buf, utf8, 3);
+ } else {
+ utf8[0] = 0xf0 | (c >> 18);
+ utf8[1] = 0x80 | ((c >> 12) & 0x3f);
+ utf8[2] = 0x80 | ((c >> 6) & 0x3f);
+ utf8[3] = 0x80 | (c & 0x3f);
+ buf_append_bytes(buf, utf8, 4);
+ }
+ }
+ utf8[0] = 0;
+ buf_append_bytes(buf, utf8, 1);
+}
+
+int get_utf8char(const char **p)
+{
+ const char *utf8 = *p;
+ unsigned char c;
+ int utfchar, nr_extra, min;
+
+ c = *(utf8++);
+ if (c < 128) {
+ utfchar = c;
+ nr_extra = 0;
+ min = 0;
+ } else if ((c & 0xe0) == 0xc0) {
+ utfchar = c & 0x1f;
+ nr_extra = 1;
+ min = 0x80;
+ } else if ((c & 0xf0) == 0xe0) {
+ utfchar = c & 0x0f;
+ nr_extra = 2;
+ min = 0x800;
+ } else if ((c & 0xf8) == 0xf0) {
+ utfchar = c & 0x07;
+ nr_extra = 3;
+ min = 0x10000;
+ } else {
+ return -EILSEQ;
+ }
+
+ while (nr_extra--) {
+ c = *(utf8++);
+ if ((c & 0xc0) != 0x80)
+ return -EILSEQ;
+
+ utfchar <<= 6;
+ utfchar |= (c & 0x3f);
+ }
+ if (utfchar > 0x10ffff || utfchar < min)
+ return -EILSEQ;
+
+ *p = utf8;
+ return utfchar;
+}
+
+int buf_append_utf16le(struct oc_text_buf *buf, const char *utf8)
+{
+ int utfchar, len = 0;
+
+ /* Ick. Now I'm implementing my own UTF8 handling too. Perhaps it's
+ time to bite the bullet and start requiring something like glib? */
+ while (*utf8) {
+ utfchar = get_utf8char(&utf8);
+ if (utfchar < 0) {
+ if (buf)
+ buf->error = utfchar;
+ return utfchar;
+ }
+ if (!buf)
+ continue;
+
+ if (utfchar >= 0x10000) {
+ utfchar -= 0x10000;
+ if (buf_ensure_space(buf, 4))
+ return buf_error(buf);
+ store_le16(buf->data + buf->pos, (utfchar >> 10) | 0xd800);
+ store_le16(buf->data + buf->pos + 2, (utfchar & 0x3ff) | 0xdc00);
+ buf->pos += 4;
+ len += 4;
+ } else {
+ if (buf_ensure_space(buf, 2))
+ return buf_error(buf);
+ store_le16(buf->data + buf->pos, utfchar);
+ buf->pos += 2;
+ len += 2;
+ }
+ }
+
+ /* We were only being used for validation */
+ if (!buf)
+ return 0;
+
+ /* Ensure UTF16 is NUL-terminated */
+ if (buf_ensure_space(buf, 2))
+ return buf_error(buf);
+ buf->data[buf->pos] = buf->data[buf->pos + 1] = 0;
+
+ return len;
+}
+
+/* Ick. Yet another wheel to reinvent. But although we could pull it
+ in from OpenSSL, we can't from GnuTLS */
+
+static inline int b64_char(char c)
+{
+ if (c >= 'A' && c <= 'Z')
+ return c - 'A';
+ if (c >= 'a' && c <= 'z')
+ return c - 'a' + 26;
+ if (c >= '0' && c <= '9')
+ return c - '0' + 52;
+ if (c == '+')
+ return 62;
+ if (c == '/')
+ return 63;
+ return -1;
+}
+
+void *openconnect_base64_decode(int *ret_len, const char *in)
+{
+ unsigned char *buf;
+ int b[4];
+ int len = strlen(in);
+
+ if (len & 3) {
+ *ret_len = -EINVAL;
+ return NULL;
+ }
+ len = (len * 3) / 4;
+ buf = malloc(len);
+ if (!buf) {
+ *ret_len = -ENOMEM;
+ return NULL;
+ }
+
+ len = 0;
+ while (*in) {
+ if (!in[1] || !in[2] || !in[3])
+ goto err;
+ b[0] = b64_char(in[0]);
+ b[1] = b64_char(in[1]);
+ if (b[0] < 0 || b[1] < 0)
+ goto err;
+ buf[len++] = (b[0] << 2) | (b[1] >> 4);
+
+ if (in[2] == '=') {
+ if (in[3] != '=' || in[4] != 0)
+ goto err;
+ break;
+ }
+ b[2] = b64_char(in[2]);
+ if (b[2] < 0)
+ goto err;
+ buf[len++] = (b[1] << 4) | (b[2] >> 2);
+ if (in[3] == '=') {
+ if (in[4] != 0)
+ goto err;
+ break;
+ }
+ b[3] = b64_char(in[3]);
+ if (b[3] < 0)
+ goto err;
+ buf[len++] = (b[2] << 6) | b[3];
+ in += 4;
+ }
+ *ret_len = len;
+ return buf;
+
+ err:
+ free(buf);
+ *ret_len = -EINVAL;
+ return NULL;
+}
+
+static const char b64_table[] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
+};
+
+void buf_append_base64(struct oc_text_buf *buf, const void *bytes, int len,
+ int line_len)
+{
+ const unsigned char *in = bytes;
+ int hibits;
+
+ if (!buf || buf->error)
+ return;
+
+ unsigned int needed = ((len + 2u) / 3) * 4 + 1;
+ if (line_len)
+ needed += needed / line_len;
+
+ if (needed >= (unsigned)(INT_MAX - buf->pos)) {
+ buf->error = -E2BIG;
+ return;
+ }
+
+ if (buf_ensure_space(buf, needed))
+ return;
+
+ int ll = 0;
+ while (len > 0) {
+ if (line_len) {
+ ll += 4;
+ if (ll >= line_len) {
+ ll = 0;
+ buf->data[buf->pos++] = '\n';
+ }
+ }
+
+ buf->data[buf->pos++] = b64_table[in[0] >> 2];
+ hibits = (in[0] << 4) & 0x30;
+ if (len == 1) {
+ buf->data[buf->pos++] = b64_table[hibits];
+ buf->data[buf->pos++] = '=';
+ buf->data[buf->pos++] = '=';
+ break;
+ }
+ buf->data[buf->pos++] = b64_table[hibits | (in[1] >> 4)];
+ hibits = (in[1] << 2) & 0x3c;
+ if (len == 2) {
+ buf->data[buf->pos++] = b64_table[hibits];
+ buf->data[buf->pos++] = '=';
+ break;
+ }
+ buf->data[buf->pos++] = b64_table[hibits | (in[2] >> 6)];
+ buf->data[buf->pos++] = b64_table[in[2] & 0x3f];
+ in += 3;
+ len -= 3;
+ }
+ buf->data[buf->pos] = 0;
+}