summaryrefslogtreecommitdiffstats
path: root/src/util/support
diff options
context:
space:
mode:
authorSam Hartman <hartmans@mit.edu>2009-01-03 23:19:42 +0000
committerSam Hartman <hartmans@mit.edu>2009-01-03 23:19:42 +0000
commit0ba5ccd7bb3ea15e44a87f84ca6feed8890f657d (patch)
tree2049c9c2cb135fe36b14c0a171711259258d18ec /src/util/support
parentff0a6514c9f4230938c29922d69cbd4e83691adf (diff)
downloadkrb5-0ba5ccd7bb3ea15e44a87f84ca6feed8890f657d.tar.gz
krb5-0ba5ccd7bb3ea15e44a87f84ca6feed8890f657d.tar.xz
krb5-0ba5ccd7bb3ea15e44a87f84ca6feed8890f657d.zip
Merge mskrb-integ onto trunk
The mskrb-integ branch includes support for the following projects: Projects/Aliases * Projects/PAC and principal APIs * Projects/AEAD encryption API * Projects/GSSAPI DCE * Projects/RFC 3244 In addition, it includes support for enctype negotiation, and a variety of GSS-API extensions. In the KDC it includes support for protocol transition, constrained delegation and a new authorization data interface. The old authorization data interface is also supported. This commit merges the mskrb-integ branch on to the trunk. Additional review and testing is required. Merge commit 'mskrb-integ' into trunk ticket: new status: open git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@21690 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/util/support')
-rw-r--r--src/util/support/Makefile.in13
-rw-r--r--src/util/support/libkrb5support-fixed.exports8
-rw-r--r--src/util/support/utf8.c539
-rw-r--r--src/util/support/utf8_conv.c452
4 files changed, 1012 insertions, 0 deletions
diff --git a/src/util/support/Makefile.in b/src/util/support/Makefile.in
index 3cede4394..674a04fe2 100644
--- a/src/util/support/Makefile.in
+++ b/src/util/support/Makefile.in
@@ -44,6 +44,8 @@ STLIBOBJS= \
k5buf.o \
gmt_mktime.o \
fake-addrinfo.o \
+ utf8.o \
+ utf8_conv.o \
$(STRLCPY_ST_OBJ) \
$(PRINTF_ST_OBJ) \
$(MKSTEMP_ST_OBJ)
@@ -56,6 +58,8 @@ LIBOBJS= \
$(OUTPRE)k5buf.$(OBJEXT) \
$(OUTPRE)gmt_mktime.$(OBJEXT) \
$(OUTPRE)fake-addrinfo.$(OBJEXT) \
+ $(OUTPRE)utf8.$(OBJEXT) \
+ $(OUTPRE)utf8_conv.$(OBJEXT) \
$(STRLCPY_OBJ) \
$(PRINTF_OBJ) \
$(MKSTEMP_OBJ)
@@ -73,6 +77,8 @@ SRCS=\
$(srcdir)/k5buf.c \
$(srcdir)/gmt_mktime.c \
$(srcdir)/fake-addrinfo.c \
+ $(srcdir)/utf8.c \
+ $(srcdir)/utf8_conv.c \
$(srcdir)/strlcpy.c \
$(srcdir)/printf.c \
$(srcdir)/mkstemp.c \
@@ -166,6 +172,13 @@ fake-addrinfo.so fake-addrinfo.po $(OUTPRE)fake-addrinfo.$(OBJEXT): \
$(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
$(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
cache-addrinfo.h fake-addrinfo.c supp-int.h
+utf8.so utf8.po $(OUTPRE)utf8.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
+ $(SRCTOP)/include/k5-utf8.h supp-int.h utf8.c
+utf8_conv.so utf8_conv.po $(OUTPRE)utf8_conv.$(OBJEXT): \
+ $(BUILDTOP)/include/autoconf.h $(SRCTOP)/include/k5-platform.h \
+ $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/k5-utf8.h \
+ supp-int.h utf8_conv.c
strlcpy.so strlcpy.po $(OUTPRE)strlcpy.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
strlcpy.c
diff --git a/src/util/support/libkrb5support-fixed.exports b/src/util/support/libkrb5support-fixed.exports
index fef8fbac7..4638bb659 100644
--- a/src/util/support/libkrb5support-fixed.exports
+++ b/src/util/support/libkrb5support-fixed.exports
@@ -38,3 +38,11 @@ krb5int_buf_truncate
krb5int_buf_data
krb5int_buf_len
krb5int_free_buf
+krb5int_utf8cs_to_ucs2les
+krb5int_utf8s_to_ucs2les
+krb5int_ucs2lecs_to_utf8s
+krb5int_ucs4_to_utf8
+krb5int_utf8_to_ucs4
+krb5int_utf8_lentab
+krb5int_utf8_mintab
+krb5int_utf8_next
diff --git a/src/util/support/utf8.c b/src/util/support/utf8.c
new file mode 100644
index 000000000..8712c6751
--- /dev/null
+++ b/src/util/support/utf8.c
@@ -0,0 +1,539 @@
+/*
+ * util/support/utf8.c
+ *
+ * Copyright 2008 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2008 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Basic UTF-8 routines
+ *
+ * These routines are "dumb". Though they understand UTF-8,
+ * they don't grok Unicode. That is, they can push bits,
+ * but don't have a clue what the bits represent. That's
+ * good enough for use with the KRB5 Client SDK.
+ *
+ * These routines are not optimized.
+ */
+
+#include "k5-platform.h"
+#include "k5-utf8.h"
+#include "supp-int.h"
+
+/*
+ * return the number of bytes required to hold the
+ * NULL-terminated UTF-8 string NOT INCLUDING the
+ * termination.
+ */
+size_t krb5int_utf8_bytes(const char *p)
+{
+ size_t bytes;
+
+ for (bytes = 0; p[bytes]; bytes++)
+ ;
+
+ return bytes;
+}
+
+size_t krb5int_utf8_chars(const char *p)
+{
+ /* could be optimized and could check for invalid sequences */
+ size_t chars = 0;
+
+ for ( ; *p ; KRB5_UTF8_INCR(p))
+ chars++;
+
+ return chars;
+}
+
+size_t krb5int_utf8c_chars(const char *p, size_t length)
+{
+ /* could be optimized and could check for invalid sequences */
+ size_t chars = 0;
+ const char *end = p + length;
+
+ for ( ; p < end; KRB5_UTF8_INCR(p))
+ chars++;
+
+ return chars;
+}
+
+/* return offset to next character */
+int krb5int_utf8_offset(const char *p)
+{
+ return KRB5_UTF8_NEXT(p) - p;
+}
+
+/*
+ * Returns length indicated by first byte.
+ */
+const char krb5int_utf8_lentab[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 0, 0 };
+
+int krb5int_utf8_charlen(const char *p)
+{
+ if (!(*p & 0x80))
+ return 1;
+
+ return krb5int_utf8_lentab[*(const unsigned char *)p ^ 0x80];
+}
+
+/*
+ * Make sure the UTF-8 char used the shortest possible encoding
+ * returns charlen if valid, 0 if not.
+ *
+ * Here are the valid UTF-8 encodings, taken from RFC 2279 page 4.
+ * The table is slightly modified from that of the RFC.
+ *
+ * UCS-4 range (hex) UTF-8 sequence (binary)
+ * 0000 0000-0000 007F 0.......
+ * 0000 0080-0000 07FF 110++++. 10......
+ * 0000 0800-0000 FFFF 1110++++ 10+..... 10......
+ * 0001 0000-001F FFFF 11110+++ 10++.... 10...... 10......
+ * 0020 0000-03FF FFFF 111110++ 10+++... 10...... 10...... 10......
+ * 0400 0000-7FFF FFFF 1111110+ 10++++.. 10...... 10...... 10...... 10......
+ *
+ * The '.' bits are "don't cares". When validating a UTF-8 sequence,
+ * at least one of the '+' bits must be set, otherwise the character
+ * should have been encoded in fewer octets. Note that in the two-octet
+ * case, only the first octet needs to be validated, and this is done
+ * in the krb5int_utf8_lentab[] above.
+ */
+
+/* mask of required bits in second octet */
+#undef c
+#define c const char
+c krb5int_utf8_mintab[] = {
+ (c)0x20, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80,
+ (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80,
+ (c)0x30, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80,
+ (c)0x38, (c)0x80, (c)0x80, (c)0x80, (c)0x3c, (c)0x80, (c)0x00, (c)0x00 };
+#undef c
+
+int krb5int_utf8_charlen2(const char *p)
+{
+ int i = KRB5_UTF8_CHARLEN(p);
+
+ if (i > 2) {
+ if (!(krb5int_utf8_mintab[*p & 0x1f] & p[1]))
+ i = 0;
+ }
+
+ return i;
+}
+
+krb5_ucs4 krb5int_utf8_to_ucs4(const char *p)
+{
+ const unsigned char *c = (const unsigned char *) p;
+ krb5_ucs4 ch;
+ int len, i;
+ static unsigned char mask[] = {
+ 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 };
+
+ len = KRB5_UTF8_CHARLEN2(p, len);
+
+ if (len == 0)
+ return KRB5_UCS4_INVALID;
+
+ ch = c[0] & mask[len];
+
+ for (i = 1; i < len; i++) {
+ if ((c[i] & 0xc0) != 0x80) {
+ return KRB5_UCS4_INVALID;
+ }
+
+ ch <<= 6;
+ ch |= c[i] & 0x3f;
+ }
+
+ return ch;
+}
+
+krb5_ucs2 krb5int_utf8_to_ucs2(const char *p)
+{
+ krb5_ucs4 ch = krb5int_utf8_to_ucs4(p);
+
+ if (ch == KRB5_UCS4_INVALID || ch > SHRT_MAX)
+ return KRB5_UCS2_INVALID;
+
+ return (krb5_ucs2)ch;
+}
+
+/* conv UCS-2 to UTF-8, not used */
+size_t krb5int_ucs4_to_utf8(krb5_ucs4 c, char *buf)
+{
+ size_t len = 0;
+ unsigned char *p = (unsigned char *) buf;
+
+ /* not a valid Unicode character */
+ if (c < 0)
+ return 0;
+
+ /* Just return length, don't convert */
+ if (buf == NULL) {
+ if (c < 0x80) return 1;
+ else if (c < 0x800) return 2;
+ else if (c < 0x10000) return 3;
+ else if (c < 0x200000) return 4;
+ else if (c < 0x4000000) return 5;
+ else return 6;
+ }
+
+ if (c < 0x80) {
+ p[len++] = c;
+ } else if (c < 0x800) {
+ p[len++] = 0xc0 | ( c >> 6 );
+ p[len++] = 0x80 | ( c & 0x3f );
+ } else if (c < 0x10000) {
+ p[len++] = 0xe0 | ( c >> 12 );
+ p[len++] = 0x80 | ( (c >> 6) & 0x3f );
+ p[len++] = 0x80 | ( c & 0x3f );
+ } else if (c < 0x200000) {
+ p[len++] = 0xf0 | ( c >> 18 );
+ p[len++] = 0x80 | ( (c >> 12) & 0x3f );
+ p[len++] = 0x80 | ( (c >> 6) & 0x3f );
+ p[len++] = 0x80 | ( c & 0x3f );
+ } else if (c < 0x4000000) {
+ p[len++] = 0xf8 | ( c >> 24 );
+ p[len++] = 0x80 | ( (c >> 18) & 0x3f );
+ p[len++] = 0x80 | ( (c >> 12) & 0x3f );
+ p[len++] = 0x80 | ( (c >> 6) & 0x3f );
+ p[len++] = 0x80 | ( c & 0x3f );
+ } else /* if( c < 0x80000000 ) */ {
+ p[len++] = 0xfc | ( c >> 30 );
+ p[len++] = 0x80 | ( (c >> 24) & 0x3f );
+ p[len++] = 0x80 | ( (c >> 18) & 0x3f );
+ p[len++] = 0x80 | ( (c >> 12) & 0x3f );
+ p[len++] = 0x80 | ( (c >> 6) & 0x3f );
+ p[len++] = 0x80 | ( c & 0x3f );
+ }
+
+ return len;
+}
+
+size_t krb5int_ucs2_to_utf8(krb5_ucs2 c, char *buf)
+{
+ return krb5int_ucs4_to_utf8((krb5_ucs4)c, buf);
+}
+
+#define KRB5_UCS_UTF8LEN(c) \
+ c < 0 ? 0 : (c < 0x80 ? 1 : (c < 0x800 ? 2 : (c < 0x10000 ? 3 : \
+ (c < 0x200000 ? 4 : (c < 0x4000000 ? 5 : 6)))))
+
+/*
+ * Advance to the next UTF-8 character
+ *
+ * Ignores length of multibyte character, instead rely on
+ * continuation markers to find start of next character.
+ * This allows for "resyncing" of when invalid characters
+ * are provided provided the start of the next character
+ * is appears within the 6 bytes examined.
+ */
+char *krb5int_utf8_next(const char *p)
+{
+ int i;
+ const unsigned char *u = (const unsigned char *) p;
+
+ if (KRB5_UTF8_ISASCII(u)) {
+ return (char *) &p[1];
+ }
+
+ for (i = 1; i < 6; i++) {
+ if ((u[i] & 0xc0) != 0x80) {
+ return (char *) &p[i];
+ }
+ }
+
+ return (char *) &p[i];
+}
+
+/*
+ * Advance to the previous UTF-8 character
+ *
+ * Ignores length of multibyte character, instead rely on
+ * continuation markers to find start of next character.
+ * This allows for "resyncing" of when invalid characters
+ * are provided provided the start of the next character
+ * is appears within the 6 bytes examined.
+ */
+char *krb5int_utf8_prev(const char *p)
+{
+ int i;
+ const unsigned char *u = (const unsigned char *) p;
+
+ for (i = -1; i>-6 ; i--) {
+ if ((u[i] & 0xc0 ) != 0x80) {
+ return (char *) &p[i];
+ }
+ }
+
+ return (char *) &p[i];
+}
+
+/*
+ * Copy one UTF-8 character from src to dst returning
+ * number of bytes copied.
+ *
+ * Ignores length of multibyte character, instead rely on
+ * continuation markers to find start of next character.
+ * This allows for "resyncing" of when invalid characters
+ * are provided provided the start of the next character
+ * is appears within the 6 bytes examined.
+ */
+int krb5int_utf8_copy(char* dst, const char *src)
+{
+ int i;
+ const unsigned char *u = (const unsigned char *) src;
+
+ dst[0] = src[0];
+
+ if (KRB5_UTF8_ISASCII(u)) {
+ return 1;
+ }
+
+ for (i=1; i<6; i++) {
+ if ((u[i] & 0xc0) != 0x80) {
+ return i;
+ }
+ dst[i] = src[i];
+ }
+
+ return i;
+}
+
+#ifndef UTF8_ALPHA_CTYPE
+/*
+ * UTF-8 ctype routines
+ * Only deals with characters < 0x80 (ie: US-ASCII)
+ */
+
+int krb5int_utf8_isascii(const char * p)
+{
+ unsigned c = * (const unsigned char *) p;
+
+ return KRB5_ASCII(c);
+}
+
+int krb5int_utf8_isdigit(const char * p)
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if (!KRB5_ASCII(c))
+ return 0;
+
+ return KRB5_DIGIT( c );
+}
+
+int krb5int_utf8_isxdigit(const char * p)
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if (!KRB5_ASCII(c))
+ return 0;
+
+ return KRB5_HEX(c);
+}
+
+int krb5int_utf8_isspace(const char * p)
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if (!KRB5_ASCII(c))
+ return 0;
+
+ switch(c) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ case '\v':
+ case '\f':
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * These are not needed by the C SDK and are
+ * not "good enough" for general use.
+ */
+int krb5int_utf8_isalpha(const char * p)
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if (!KRB5_ASCII(c))
+ return 0;
+
+ return KRB5_ALPHA(c);
+}
+
+int krb5int_utf8_isalnum(const char * p)
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if (!KRB5_ASCII(c))
+ return 0;
+
+ return KRB5_ALNUM(c);
+}
+
+int krb5int_utf8_islower(const char * p)
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if (!KRB5_ASCII(c))
+ return 0;
+
+ return KRB5_LOWER(c);
+}
+
+int krb5int_utf8_isupper(const char * p)
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if (!KRB5_ASCII(c))
+ return 0;
+
+ return KRB5_UPPER(c);
+}
+#endif
+
+
+/*
+ * UTF-8 string routines
+ */
+
+/* like strchr() */
+char *krb5int_utf8_strchr(const char *str, const char *chr)
+{
+ for ( ; *str != '\0'; KRB5_UTF8_INCR(str)) {
+ if (krb5int_utf8_to_ucs4(str) == krb5int_utf8_to_ucs4(chr)) {
+ return (char *)str;
+ }
+ }
+
+ return NULL;
+}
+
+/* like strcspn() but returns number of bytes, not characters */
+size_t krb5int_utf8_strcspn(const char *str, const char *set)
+{
+ const char *cstr;
+ const char *cset;
+
+ for (cstr = str; *cstr != '\0'; KRB5_UTF8_INCR(cstr)) {
+ for (cset = set; *cset != '\0'; KRB5_UTF8_INCR(cset)) {
+ if (krb5int_utf8_to_ucs4(cstr) == krb5int_utf8_to_ucs4(cset)) {
+ return cstr - str;
+ }
+ }
+ }
+
+ return cstr - str;
+}
+
+/* like strspn() but returns number of bytes, not characters */
+size_t krb5int_utf8_strspn(const char *str, const char *set)
+{
+ const char *cstr;
+ const char *cset;
+
+ for (cstr = str; *cstr != '\0'; KRB5_UTF8_INCR(cstr)) {
+ for (cset = set; ; KRB5_UTF8_INCR(cset)) {
+ if (*cset == '\0') {
+ return cstr - str;
+ }
+
+ if (krb5int_utf8_to_ucs4(cstr) == krb5int_utf8_to_ucs4(cset)) {
+ break;
+ }
+ }
+ }
+
+ return cstr - str;
+}
+
+/* like strpbrk(), replaces strchr() as well */
+char *krb5int_utf8_strpbrk(const char *str, const char *set)
+{
+ for ( ; *str != '\0'; KRB5_UTF8_INCR(str)) {
+ const char *cset;
+
+ for (cset = set; *cset != '\0'; KRB5_UTF8_INCR(cset)) {
+ if (krb5int_utf8_to_ucs4(str) == krb5int_utf8_to_ucs4(cset)) {
+ return (char *)str;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/* like strtok_r(), not strtok() */
+char *krb5int_utf8_strtok(char *str, const char *sep, char **last)
+{
+ char *begin;
+ char *end;
+
+ if (last == NULL)
+ return NULL;
+
+ begin = str ? str : *last;
+
+ begin += krb5int_utf8_strspn(begin, sep);
+
+ if (*begin == '\0') {
+ *last = NULL;
+ return NULL;
+ }
+
+ end = &begin[krb5int_utf8_strcspn(begin, sep)];
+
+ if (*end != '\0') {
+ char *next = KRB5_UTF8_NEXT(end);
+ *end = '\0';
+ end = next;
+ }
+
+ *last = end;
+
+ return begin;
+}
diff --git a/src/util/support/utf8_conv.c b/src/util/support/utf8_conv.c
new file mode 100644
index 000000000..a8c9267ef
--- /dev/null
+++ b/src/util/support/utf8_conv.c
@@ -0,0 +1,452 @@
+/*
+ * util/support/utf8_conv.c
+ *
+ * Copyright 2008 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2008 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
+ *
+ * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND
+ * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT
+ * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS
+ * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE"
+ * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION
+ * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP
+ * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT
+ * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
+ */
+
+/*
+ * UTF-8 Conversion Routines
+ *
+ * These routines convert between Wide Character and UTF-8,
+ * or between MultiByte and UTF-8 encodings.
+ *
+ * Both single character and string versions of the functions are provided.
+ * All functions return -1 if the character or string cannot be converted.
+ */
+
+#include "k5-platform.h"
+#include "k5-utf8.h"
+#include "supp-int.h"
+
+static unsigned char mask[] = { 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 };
+
+static ssize_t
+k5_utf8s_to_ucs2s(krb5_ucs2 *ucs2str,
+ const char *utf8str,
+ size_t count,
+ int little_endian)
+{
+ size_t ucs2len = 0;
+ size_t utflen, i;
+ krb5_ucs2 ch;
+
+ /* If input ptr is NULL or empty... */
+ if (utf8str == NULL || *utf8str == '\0') {
+ *ucs2str = 0;
+
+ return 0;
+ }
+
+ /* Examine next UTF-8 character. */
+ while (*utf8str && ucs2len < count) {
+ /* Get UTF-8 sequence length from 1st byte */
+ utflen = KRB5_UTF8_CHARLEN2(utf8str, utflen);
+
+ if (utflen == 0 || utflen > KRB5_MAX_UTF8_LEN)
+ return -1;
+
+ /* First byte minus length tag */
+ ch = (krb5_ucs2)(utf8str[0] & mask[utflen]);
+
+ for (i = 1; i < utflen; i++) {
+ /* Subsequent bytes must start with 10 */
+ if ((utf8str[i] & 0xc0) != 0x80)
+ return -1;
+
+ ch <<= 6; /* 6 bits of data in each subsequent byte */
+ ch |= (krb5_ucs2)(utf8str[i] & 0x3f);
+ }
+
+ if (ucs2str != NULL) {
+#ifdef K5_BE
+ if (little_endian)
+ ucs2str[ucs2len] = SWAP16(ch);
+ else
+#endif
+ ucs2str[ucs2len] = ch;
+ }
+
+ utf8str += utflen; /* Move to next UTF-8 character */
+ ucs2len++; /* Count number of wide chars stored/required */
+ }
+
+ assert(ucs2len < count);
+
+ /* Add null terminator if there's room in the buffer. */
+ ucs2str[ucs2len] = 0;
+
+ return ucs2len;
+}
+
+int
+krb5int_utf8s_to_ucs2s(const char *utf8s,
+ krb5_ucs2 **ucs2s,
+ size_t *ucs2chars)
+{
+ ssize_t len;
+ size_t chars;
+
+ chars = krb5int_utf8_chars(utf8s);
+ *ucs2s = (krb5_ucs2 *)malloc((chars + 1) * sizeof(krb5_ucs2));
+ if (*ucs2s == NULL) {
+ return ENOMEM;
+ }
+
+ len = k5_utf8s_to_ucs2s(*ucs2s, utf8s, chars + 1, 0);
+ if (len < 0) {
+ free(*ucs2s);
+ *ucs2s = NULL;
+ return EINVAL;
+ }
+
+ if (ucs2chars != NULL) {
+ *ucs2chars = chars;
+ }
+
+ return 0;
+}
+
+int
+krb5int_utf8cs_to_ucs2s(const char *utf8s,
+ size_t utf8slen,
+ krb5_ucs2 **ucs2s,
+ size_t *ucs2chars)
+{
+ ssize_t len;
+ size_t chars;
+
+ chars = krb5int_utf8c_chars(utf8s, utf8slen);
+ *ucs2s = (krb5_ucs2 *)malloc((chars + 1) * sizeof(krb5_ucs2));
+ if (*ucs2s == NULL) {
+ return ENOMEM;
+ }
+
+ len = k5_utf8s_to_ucs2s(*ucs2s, utf8s, chars + 1, 0);
+ if (len < 0) {
+ free(*ucs2s);
+ *ucs2s = NULL;
+ return EINVAL;
+ }
+
+ if (ucs2chars != NULL) {
+ *ucs2chars = chars;
+ }
+
+ return 0;
+}
+
+int
+krb5int_utf8s_to_ucs2les(const char *utf8s,
+ unsigned char **ucs2les,
+ size_t *ucs2leslen)
+{
+ ssize_t len;
+ size_t chars;
+
+ chars = krb5int_utf8_chars(utf8s);
+
+ *ucs2les = (unsigned char *)malloc((chars + 1) * sizeof(krb5_ucs2));
+ if (*ucs2les == NULL) {
+ return ENOMEM;
+ }
+
+ len = k5_utf8s_to_ucs2s((krb5_ucs2 *)*ucs2les, utf8s, chars + 1, 1);
+ if (len < 0) {
+ free(ucs2les);
+ *ucs2les = NULL;
+ return EINVAL;
+ }
+
+ if (ucs2leslen != NULL) {
+ *ucs2leslen = chars * sizeof(krb5_ucs2);
+ }
+
+ return 0;
+}
+
+int
+krb5int_utf8cs_to_ucs2les(const char *utf8s,
+ size_t utf8slen,
+ unsigned char **ucs2les,
+ size_t *ucs2leslen)
+{
+ ssize_t len;
+ size_t chars;
+
+ chars = krb5int_utf8c_chars(utf8s, utf8slen);
+
+ *ucs2les = (unsigned char *)malloc((chars + 1) * sizeof(krb5_ucs2));
+ if (*ucs2les == NULL) {
+ return ENOMEM;
+ }
+
+ len = k5_utf8s_to_ucs2s((krb5_ucs2 *)*ucs2les, utf8s, chars + 1, 1);
+ if (len < 0) {
+ free(*ucs2les);
+ *ucs2les = NULL;
+ return EINVAL;
+ }
+
+ if (ucs2leslen != NULL) {
+ *ucs2leslen = chars * sizeof(krb5_ucs2);
+ }
+
+ return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ Convert a wide char string to a UTF-8 string.
+ No more than 'count' bytes will be written to the output buffer.
+ Return the # of bytes written to the output buffer, excl null terminator.
+
+ ucs2len is -1 if the UCS-2 string is NUL terminated, otherwise it is the
+ length of the UCS-2 string in characters
+*/
+static ssize_t
+k5_ucs2s_to_utf8s(char *utf8str, const krb5_ucs2 *ucs2str,
+ size_t count, ssize_t ucs2len, int little_endian)
+{
+ int len = 0;
+ int n;
+ char *p = utf8str;
+ krb5_ucs2 empty = 0, ch;
+
+ if (ucs2str == NULL) /* Treat input ptr NULL as an empty string */
+ ucs2str = &empty;
+
+ if (utf8str == NULL) /* Just compute size of output, excl null */
+ {
+ while (ucs2len == -1 ? *ucs2str : --ucs2len >= 0) {
+ /* Get UTF-8 size of next wide char */
+#ifdef K5_BE
+ if (little_endian)
+ ch = SWAP16(*ucs2str++);
+ else
+#endif
+ ch = *ucs2str++;
+
+ n = krb5int_ucs2_to_utf8(ch, NULL);
+ if (n < 1)
+ return -1;
+ if (len + n < len)
+ return -1; /* overflow */
+ len += n;
+ }
+
+ return len;
+ }
+
+ /* Do the actual conversion. */
+
+ n = 1; /* In case of empty ucs2str */
+ while (ucs2len == -1 ? *ucs2str != 0 : --ucs2len >= 0) {
+#ifdef K5_BE
+ if (little_endian)
+ ch = SWAP16(*ucs2str++);
+ else
+#endif
+ ch = *ucs2str++;
+
+ n = krb5int_ucs2_to_utf8(ch, p);
+
+ if (n < 1)
+ break;
+
+ p += n;
+ count -= n; /* Space left in output buffer */
+ }
+
+ /* If not enough room for last character, pad remainder with null
+ so that return value = original count, indicating buffer full. */
+ if (n == 0) {
+ while (count--)
+ *p++ = 0;
+ }
+ /* Add a null terminator if there's room. */
+ else if (count)
+ *p = 0;
+
+ if (n == -1) /* Conversion encountered invalid wide char. */
+ return -1;
+
+ /* Return the number of bytes written to output buffer, excl null. */
+ return (p - utf8str);
+}
+
+int
+krb5int_ucs2s_to_utf8s(const krb5_ucs2 *ucs2s,
+ char **utf8s,
+ size_t *utf8slen)
+{
+ ssize_t len;
+
+ len = k5_ucs2s_to_utf8s(NULL, ucs2s, 0, -1, 0);
+ if (len < 0) {
+ return EINVAL;
+ }
+
+ *utf8s = (char *)malloc((size_t)len + 1);
+ if (*utf8s == NULL) {
+ return ENOMEM;
+ }
+
+ len = k5_ucs2s_to_utf8s(*utf8s, ucs2s, (size_t)len + 1, -1, 0);
+ if (len < 0) {
+ free(*utf8s);
+ *utf8s = NULL;
+ return EINVAL;
+ }
+
+ if (utf8slen != NULL) {
+ *utf8slen = len;
+ }
+
+ return 0;
+}
+
+int
+krb5int_ucs2les_to_utf8s(const unsigned char *ucs2les,
+ char **utf8s,
+ size_t *utf8slen)
+{
+ ssize_t len;
+
+ len = k5_ucs2s_to_utf8s(NULL, (krb5_ucs2 *)ucs2les, 0, -1, 1);
+ if (len < 0)
+ return EINVAL;
+
+ *utf8s = (char *)malloc((size_t)len + 1);
+ if (*utf8s == NULL) {
+ return ENOMEM;
+ }
+
+ len = k5_ucs2s_to_utf8s(*utf8s, (krb5_ucs2 *)ucs2les, (size_t)len + 1, -1, 1);
+ if (len < 0) {
+ free(*utf8s);
+ *utf8s = NULL;
+ return EINVAL;
+ }
+
+ if (utf8slen != NULL) {
+ *utf8slen = len;
+ }
+
+ return 0;
+}
+
+int
+krb5int_ucs2cs_to_utf8s(const krb5_ucs2 *ucs2s,
+ size_t ucs2slen,
+ char **utf8s,
+ size_t *utf8slen)
+{
+ ssize_t len;
+
+ if (ucs2slen > SSIZE_MAX)
+ return ERANGE;
+
+ len = k5_ucs2s_to_utf8s(NULL, (krb5_ucs2 *)ucs2s, 0,
+ (ssize_t)ucs2slen, 0);
+ if (len < 0)
+ return EINVAL;
+
+ *utf8s = (char *)malloc((size_t)len + 1);
+ if (*utf8s == NULL) {
+ return ENOMEM;
+ }
+
+ len = k5_ucs2s_to_utf8s(*utf8s, (krb5_ucs2 *)ucs2s,
+ (size_t)len + 1, (ssize_t)ucs2slen, 0);
+ if (len < 0) {
+ free(*utf8s);
+ *utf8s = NULL;
+ return EINVAL;
+ }
+
+ if (utf8slen != NULL) {
+ *utf8slen = len;
+ }
+
+ return 0;
+}
+
+int
+krb5int_ucs2lecs_to_utf8s(const unsigned char *ucs2les,
+ size_t ucs2leslen,
+ char **utf8s,
+ size_t *utf8slen)
+{
+ ssize_t len;
+
+ if (ucs2leslen > SSIZE_MAX)
+ return ERANGE;
+
+ len = k5_ucs2s_to_utf8s(NULL, (krb5_ucs2 *)ucs2les, 0,
+ (ssize_t)ucs2leslen, 1);
+ if (len < 0)
+ return EINVAL;
+
+ *utf8s = (char *)malloc((size_t)len + 1);
+ if (*utf8s == NULL) {
+ return ENOMEM;
+ }
+
+ len = k5_ucs2s_to_utf8s(*utf8s, (krb5_ucs2 *)ucs2les,
+ (size_t)len + 1, (ssize_t)ucs2leslen, 1);
+ if (len < 0) {
+ free(*utf8s);
+ *utf8s = NULL;
+ return EINVAL;
+ }
+
+ if (utf8slen != NULL) {
+ *utf8slen = len;
+ }
+
+ return 0;
+}
+