diff options
| author | Sam Hartman <hartmans@mit.edu> | 2009-01-03 23:19:42 +0000 |
|---|---|---|
| committer | Sam Hartman <hartmans@mit.edu> | 2009-01-03 23:19:42 +0000 |
| commit | 0ba5ccd7bb3ea15e44a87f84ca6feed8890f657d (patch) | |
| tree | 2049c9c2cb135fe36b14c0a171711259258d18ec /src/util/support | |
| parent | ff0a6514c9f4230938c29922d69cbd4e83691adf (diff) | |
| download | krb5-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.in | 13 | ||||
| -rw-r--r-- | src/util/support/libkrb5support-fixed.exports | 8 | ||||
| -rw-r--r-- | src/util/support/utf8.c | 539 | ||||
| -rw-r--r-- | src/util/support/utf8_conv.c | 452 |
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 = ∅ + + 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; +} + |
