From ca6b62244d7b487656e464fe441efe6ed9032268 Mon Sep 17 00:00:00 2001 From: Rich Megginson Date: Fri, 4 Oct 2013 08:14:25 -0600 Subject: [PATCH] Ticket #47415 "Manage certificates" crashes admin server https://fedorahosted.org/389/ticket/47415 Reviewed by: ??? Branch: master Fix Description: Use the mozldap version of ldap_explode*. Copied the code from dsgw, and modified it slightly to work with adminutil. Also added checking for NULL where admldap_ldap_explode is used, and added some rudimentary debug printing. Platforms tested: RHEL6 x86_64 Flag Day: no Doc impact: no --- include/libadminutil/admutil-int.h | 3 + lib/libadminutil/admutil.c | 349 ++++++++++++++++++++++++++++++++++-- lib/libadminutil/uginfo.c | 30 +++- 3 files changed, 368 insertions(+), 14 deletions(-) diff --git a/include/libadminutil/admutil-int.h b/include/libadminutil/admutil-int.h index ef38230..8048b6d 100644 --- a/include/libadminutil/admutil-int.h +++ b/include/libadminutil/admutil-int.h @@ -187,4 +187,7 @@ int admutil_ldap_bind( int *msgidp /* pass in non-NULL for async handling */ ); +char **admldap_ldap_explode( const char *dn, const int notypes, const int nametype ); +char **admldap_ldap_explode_dn( const char *dn, const int notypes ); +char **admldap_ldap_explode_rdn( const char *rdn, const int notypes ); #endif /* __ADMUTIL_INT_H__ */ diff --git a/lib/libadminutil/admutil.c b/lib/libadminutil/admutil.c index bf04c10..f296b1f 100644 --- a/lib/libadminutil/admutil.c +++ b/lib/libadminutil/admutil.c @@ -16,6 +16,48 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * mozldap_ldap_explode, mozldap_ldap_explode_dn, mozldap_ldap_explode_rdn + * and ldap_utf8 functions + * are from the file ldap/libraries/libldap/getdn.c in the Mozilla LDAP C SDK + * + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * Copyright (c) 1994 Regents of the University of Michigan. + * All rights reserved. + * * END COPYRIGHT BLOCK **/ #include #include @@ -337,22 +379,36 @@ dn2AttrName(char* dn, char* rootDN) memset(buf, '\0', sizeof(buf)); - dnList = ldap_explode_dn(dn, 1); - rootList = ldap_explode_dn(rootDN, 1); + dnList = admldap_ldap_explode_dn(dn, 1); + rootList = admldap_ldap_explode_dn(rootDN, 1); - while (rootList[rootLen]) rootLen++; - while (dnList[dnLen]) dnLen++; + if (rootList && dnList) { + while (rootList[rootLen]) rootLen++; + while (dnList[dnLen]) dnLen++; - attrLen = dnLen - rootLen; + attrLen = dnLen - rootLen; - while (attrLen > 0) { - if (attrLen == 1) - PR_snprintf(buf, sizeof(buf), "%s%s", buf, dnList[0]); - else - PR_snprintf(buf, sizeof(buf), "%s%s.", buf, dnList[attrLen-1]); - attrLen--; + while (attrLen > 0) { + if (attrLen == 1) + PR_snprintf(buf, sizeof(buf), "%s%s", buf, dnList[0]); + else + PR_snprintf(buf, sizeof(buf), "%s%s.", buf, dnList[attrLen-1]); + attrLen--; + } + } else { + char *envvar = PR_GetEnv("ADMINUTIL_DEBUG"); + if (!dnList) { + if (envvar && (*envvar == '1')) { + fprintf(stderr, "dn2attrName: Error: invalid dn [%s]\n", dn); + } + } + if (!rootList) { + if (envvar && (*envvar == '1')) { + fprintf(stderr, "dn2attrName: Error: invalid rootDN [%s]\n", rootDN); + } + } + PL_strncpy(buf, "unknown", sizeof(buf)); } - admutil_strsFree(dnList); admutil_strsFree(rootList); @@ -380,6 +436,11 @@ attrName2dn(char* attrName, char* rootDN) rdn = attrPtr+1; } else rdn = buf; + /* NOTE: FIXME https://fedorahosted.org/389/ticket/47546 */ + /* This allows you to create bad DNs + ** no escaping for the rdn value + ** spaces after the comma + */ if (result[0] == '\0') PR_snprintf(resultPtr, sizeof(result), "cn=%s", rdn); else @@ -2533,6 +2594,270 @@ done: return rc; } +#if defined(USE_OPENLDAP) +#define LDAP_DN 1 +#define LDAP_RDN 2 + +#define INQUOTE 1 +#define OUTQUOTE 2 + +#define LDAP_UTF8LEN(s) ((0x80 & *(unsigned char*)(s)) ? ldap_utf8len (s) : 1) + +static char UTF8len[64] += {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 5, 6}; + +static char* +ldap_utf8next (char* s) + /* Return a pointer to the character immediately following *s. + Handle any valid UTF-8 character, including '\0' and ASCII. + Try to handle a misaligned pointer or a malformed character. + */ +{ + register unsigned char* next = (unsigned char*)s; + switch (UTF8len [(*next >> 2) & 0x3F]) { + case 0: /* erroneous: s points to the middle of a character. */ + case 6: if ((*++next & 0xC0) != 0x80) break; + case 5: if ((*++next & 0xC0) != 0x80) break; + case 4: if ((*++next & 0xC0) != 0x80) break; + case 3: if ((*++next & 0xC0) != 0x80) break; + case 2: if ((*++next & 0xC0) != 0x80) break; + case 1: ++next; + } + return (char*) next; +} + +static int +ldap_utf8len (const char* s) + /* Return the number of char's in the character at *s. */ +{ + return ldap_utf8next((char*)s) - s; +} + +static int +ldap_utf8isspace( char* s ) +{ + register unsigned char *c = (unsigned char*)s; + int len = ldap_utf8len(s); + + if (len == 0) { + return 0; + } else if (len == 1) { + switch (*c) { + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x20: + return 1; + default: + return 0; + } + } else if (len == 2) { + if (*c == 0xc2) { + return *(c+1) == 0x80; + } + } else if (len == 3) { + if (*c == 0xE2) { + c++; + if (*c == 0x80) { + c++; + return (*c>=0x80 && *c<=0x8a); + } + } else if (*c == 0xE3) { + return (*(c+1)==0x80) && (*(c+2)==0x80); + } else if (*c==0xEF) { + return (*(c+1)==0xBB) && (*(c+2)==0xBF); + } + return 0; + } + + /* should never reach here */ + return 0; +} + +static char ** +mozldap_ldap_explode( const char *dn, const int notypes, const int nametype ) +{ + char *p, *q, *rdnstart, **rdns = NULL; + size_t plen = 0; + int state = 0; + int count = 0; + int startquote = 0; + int endquote = 0; + int len = 0; + int goteq = 0; + + if ( dn == NULL ) { + dn = ""; + } + + while ( ldap_utf8isspace( (char *)dn )) { /* ignore leading spaces */ + ++dn; + } + + p = rdnstart = (char *) dn; + state = OUTQUOTE; + + do { + p += plen; + plen = 1; + switch ( *p ) { + case '\\': + if ( *++p == '\0' ) + p--; + else + plen = LDAP_UTF8LEN(p); + break; + case '"': + if ( state == INQUOTE ) + state = OUTQUOTE; + else + state = INQUOTE; + break; + case '+': if ( nametype != LDAP_RDN ) break; + case ';': + case ',': + case '\0': + if ( state == OUTQUOTE ) { + /* + * semicolon and comma are not valid RDN + * separators. + */ + if ( nametype == LDAP_RDN && + ( *p == ';' || *p == ',' || !goteq)) { + admutil_strsFree( rdns ); + return NULL; + } + if ( (*p == ',' || *p == ';') && !goteq ) { + /* If we get here, we have a case similar + * to =,,= + * This is not a valid dn */ + admutil_strsFree( rdns ); + return NULL; + } + goteq = 0; + ++count; + if ( rdns == NULL ) { + if (( rdns = (char **)PR_Malloc( 8 + * sizeof( char *))) == NULL ) + return( NULL ); + } else if ( count >= 8 ) { + if (( rdns = (char **)PR_Realloc( + (char *)rdns, (count+1) * + sizeof( char *))) == NULL ) + return( NULL ); + } + rdns[ count ] = NULL; + endquote = 0; + if ( notypes ) { + for ( q = rdnstart; + q < p && *q != '='; ++q ) { + ; + } + if ( q < p ) { /* *q == '=' */ + rdnstart = ++q; + } + if ( *rdnstart == '"' ) { + startquote = 1; + ++rdnstart; + } + + if ( (*(p-1) == '"') && startquote ) { + endquote = 1; + --p; + } + } + + len = p - rdnstart; + if (( rdns[ count-1 ] = (char *)PR_Calloc( + 1, len + 1 )) != NULL ) { + memcpy( rdns[ count-1 ], rdnstart, + len ); + if ( !endquote ) { + /* trim trailing spaces */ + while ( len > 0 && + ldap_utf8isspace( + &rdns[count-1][len-1] )) { + --len; + } + } + rdns[ count-1 ][ len ] = '\0'; + } + + /* + * Don't forget to increment 'p' back to where + * it should be. If we don't, then we will + * never get past an "end quote." + */ + if ( endquote == 1 ) + p++; + + rdnstart = *p ? p + 1 : p; + while ( ldap_utf8isspace( rdnstart )) + ++rdnstart; + } + break; + case '=': + if ( state == OUTQUOTE ) { + goteq = 1; + } + /* FALL */ + default: + plen = LDAP_UTF8LEN(p); + break; + } + } while ( *p ); + + return( rdns ); +} + +static char ** +mozldap_ldap_explode_dn( const char *dn, const int notypes ) +{ + return( mozldap_ldap_explode( dn, notypes, LDAP_DN ) ); +} + +static char ** +mozldap_ldap_explode_rdn( const char *rdn, const int notypes ) +{ + return( mozldap_ldap_explode( rdn, notypes, LDAP_RDN ) ); +} +#endif /* USE_OPENLDAP */ + +char ** +admldap_ldap_explode( const char *dn, const int notypes, const int nametype ) +{ +#if defined(USE_OPENLDAP) + return mozldap_ldap_explode(dn, notypes, nametype); +#else + return ldap_explode(dn, notypes, nametype); +#endif +} + +char ** +admldap_ldap_explode_dn( const char *dn, const int notypes ) +{ +#if defined(USE_OPENLDAP) + return mozldap_ldap_explode_dn(dn, notypes); +#else + return ldap_explode_dn(dn, notypes); +#endif +} + +char ** +admldap_ldap_explode_rdn( const char *rdn, const int notypes ) +{ +#if defined(USE_OPENLDAP) + return mozldap_ldap_explode_rdn(rdn, notypes); +#else + return ldap_explode_rdn(rdn, notypes); +#endif +} + /* emacs settings Local Variables: diff --git a/lib/libadminutil/uginfo.c b/lib/libadminutil/uginfo.c index 926f302..f699e57 100644 --- a/lib/libadminutil/uginfo.c +++ b/lib/libadminutil/uginfo.c @@ -169,7 +169,14 @@ admldapGetAdmGroupUGDN(char* sieDN) int dnLen = 0; char *dnbuf = NULL; - dnList = ldap_explode_dn(sieDN, 0); + dnList = admldap_ldap_explode_dn(sieDN, 0); + if (!dnList) { + char *envvar = PR_GetEnv("ADMINUTIL_DEBUG"); + if (envvar && (*envvar == '1')) { + fprintf(stderr, "Error: could not parse sieDN [%s]\n", sieDN); + } + return PL_strdup(sieDN); + } while (dnList && dnList[dnLen]) dnLen++; if (dnLen >= 5) { @@ -178,6 +185,12 @@ admldapGetAdmGroupUGDN(char* sieDN) dnList[dnLen-3], dnList[dnLen-2], dnList[dnLen-1]); + } else { + char *envvar = PR_GetEnv("ADMINUTIL_DEBUG"); + if (envvar && (*envvar == '1')) { + fprintf(stderr, "Error: invalid sieDN [%s]\n", sieDN); + } + dnbuf = PL_strdup(sieDN); } admutil_strsFree(dnList); return dnbuf; @@ -190,13 +203,26 @@ admldapGetDomainUGDN(char* sieDN) int dnLen = 0; char *dnbuf = NULL; - dnList = ldap_explode_dn(sieDN, 0); + dnList = admldap_ldap_explode_dn(sieDN, 0); + if (!dnList) { + char *envvar = PR_GetEnv("ADMINUTIL_DEBUG"); + if (envvar && (*envvar == '1')) { + fprintf(stderr, "Error: could not parse sieDN [%s]\n", sieDN); + } + return PL_strdup(sieDN); + } while (dnList && dnList[dnLen]) dnLen++; if (dnLen >= 3) { dnbuf = PR_smprintf("cn=UserDirectory,ou=Global Preferences,%s,%s", dnList[dnLen-2], dnList[dnLen-1]); + } else { + char *envvar = PR_GetEnv("ADMINUTIL_DEBUG"); + if (envvar && (*envvar == '1')) { + fprintf(stderr, "Error: invalid sieDN [%s]\n", sieDN); + } + dnbuf = PL_strdup(sieDN); } admutil_strsFree(dnList); -- 1.7.1