/* This is extracted from the OpenLDAP sources. * * Stuff that isn't used in e-book-backend-ldap.c was dropped, like * the LDAPSchemaExtensionItem stuff. * * This file basically has three parts: * * - some general macros from OpenLDAP that work as such on all * implementations. * * - ldap_str2objectclass() * * - ldap_url_parse() */ /* This work is part of OpenLDAP Software . * * Copyright 1998-2005 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 file COPYING.OPENLDAP in * the top-level directory of the distribution or, alternatively, at * . */ #include #include /* from various header files */ #define LDAP_CONST const #define LDAP_PORT 389 /* ldap:/// default LDAP port */ #define LDAPS_PORT 636 /* ldaps:/// default LDAP over TLS port */ #define LDAP_ROOT_DSE "" #define LDAP_SPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\n') #define LDAP_DIGIT(c) ((c) >= '0' && (c) <= '9') #define LDAP_EXOP_START_TLS "1.3.6.1.4.1.1466.20037" /* RFC 2830 */ #define LDAP_MALLOC(n) malloc((n)) #define LDAP_CALLOC(n,s) calloc((n),(s)) #define LDAP_REALLOC(p,s) realloc((p),(s)) #define LDAP_FREE(p) free((p)) #define LDAP_VFREE(p) vfree((gpointer *)(p)) #define LDAP_STRDUP(s) strdup((s)) #define LDAP_RANGE(n,x,y) (((x) <= (n)) && ((n) <= (y))) #define LDAP_NAME_ERROR(n) LDAP_RANGE((n),0x20,0x24) /* 32-34,36 */ #define ldap_msgtype(lm) (lm)->lm_msgtype #define ldap_msgid(lm) (lm)->lm_msgid #ifndef LDAP_TYPE_OR_VALUE_EXISTS #define LDAP_TYPE_OR_VALUE_EXISTS 0x14 #endif #ifndef LDAP_SCOPE_DEFAULT #define LDAP_SCOPE_DEFAULT -1 #endif #ifndef LDAP_OPT_SUCCESS #define LDAP_OPT_SUCCESS 0x00 #endif #ifndef LDAP_INSUFFICIENT_ACCESS #define LDAP_INSUFFICIENT_ACCESS 0x32 #endif #define LDAP_SCHERR_OUTOFMEM 1 #define LDAP_SCHERR_UNEXPTOKEN 2 #define LDAP_SCHERR_NOLEFTPAREN 3 #define LDAP_SCHERR_NORIGHTPAREN 4 #define LDAP_SCHERR_NODIGIT 5 #define LDAP_SCHERR_BADNAME 6 #define LDAP_SCHERR_BADDESC 7 #define LDAP_SCHERR_BADSUP 8 #define LDAP_SCHERR_DUPOPT 9 #define LDAP_SCHERR_EMPTY 10 #define LDAP_SCHERR_MISSING 11 #define LDAP_SCHERR_OUT_OF_ORDER 12 #define LDAP_SCHEMA_YES 1 #define LDAP_SCHEMA_ABSTRACT 0 #define LDAP_SCHEMA_STRUCTURAL 1 #define LDAP_SCHEMA_AUXILIARY 2 #define LDAP_SCHEMA_ALLOW_NONE 0x00U /* Strict parsing */ #define LDAP_SCHEMA_ALLOW_NO_OID 0x01U /* Allow missing oid */ #define LDAP_SCHEMA_ALLOW_QUOTED 0x02U /* Allow bogus extra quotes */ #define LDAP_SCHEMA_ALLOW_DESCR 0x04U /* Allow descr instead of OID */ #define LDAP_SCHEMA_ALLOW_DESCR_PREFIX 0x08U /* Allow descr as OID prefix */ #define LDAP_SCHEMA_ALLOW_OID_MACRO 0x10U /* Allow OID macros in slapd */ #define LDAP_SCHEMA_ALLOW_OUT_OF_ORDER_FIELDS 0x20U /* Allow fields in most any order */ #define LDAP_SCHEMA_ALLOW_ALL 0x3fU /* Be very liberal in parsing */ #define LDAP_SCHEMA_SKIP 0x80U /* Don't malloc any result */ typedef struct ldap_objectclass { gchar *oc_oid; /* REQUIRED */ gchar **oc_names; /* OPTIONAL */ gchar *oc_desc; /* OPTIONAL */ gint oc_obsolete; /* 0=no, 1=yes */ gchar **oc_sup_oids; /* OPTIONAL */ gint oc_kind; /* 0=ABSTRACT, 1=STRUCTURAL, 2=AUXILIARY */ gchar **oc_at_oids_must; /* OPTIONAL */ gchar **oc_at_oids_may; /* OPTIONAL */ } LDAPObjectClass; static void vfree (gpointer *vec) { gint i; for (i = 0; vec[i] != NULL; i++) free (vec[i]); } /* from schema.c */ /* * Now come the parsers. There is one parser for each entity type: * objectclasses, attributetypes, etc. * * Each of them is written as a recursive-descent parser, except that * none of them is really recursive. But the idea is kept: there * is one routine per non-terminal that eithers gobbles lexical tokens * or calls lower-level routines, etc. * * The scanner is implemented in the routine get_token. Actually, * get_token is more than a scanner and will return tokens that are * in fact non-terminals in the grammar. So you can see the whole * approach as the combination of a low-level bottom-up recognizer * combined with a scanner and a number of top-down parsers. Or just * consider that the real grammars recognized by the parsers are not * those of the standards. As a matter of fact, our parsers are more * liberal than the spec when there is no ambiguity. * * The difference is pretty academic (modulo bugs or incorrect * interpretation of the specs). */ #define TK_NOENDQUOTE -2 #define TK_OUTOFMEM -1 #define TK_EOS 0 #define TK_UNEXPCHAR 1 #define TK_BAREWORD 2 #define TK_QDSTRING 3 #define TK_LEFTPAREN 4 #define TK_RIGHTPAREN 5 #define TK_DOLLAR 6 #define TK_QDESCR TK_QDSTRING struct token { gint type; gchar *sval; }; static gint get_token (const gchar **sp, gchar **token_val) { gint kind; const gchar *p; const gchar *q; gchar *res; *token_val = NULL; switch (**sp) { case '\0': kind = TK_EOS; (*sp)++; break; case '(': kind = TK_LEFTPAREN; (*sp)++; break; case ')': kind = TK_RIGHTPAREN; (*sp)++; break; case '$': kind = TK_DOLLAR; (*sp)++; break; case '\'': kind = TK_QDSTRING; (*sp)++; p = *sp; while (**sp != '\'' && **sp != '\0') (*sp)++; if (**sp == '\'') { q = *sp; res = LDAP_MALLOC (q - p + 1); if (!res) { kind = TK_OUTOFMEM; } else { strncpy (res,p,q - p); res[q - p] = '\0'; *token_val = res; } (*sp)++; } else { kind = TK_NOENDQUOTE; } break; default: kind = TK_BAREWORD; p = *sp; while (!LDAP_SPACE (**sp) && **sp != '(' && **sp != ')' && **sp != '$' && **sp != '\'' && **sp != '\0') (*sp)++; q = *sp; res = LDAP_MALLOC (q - p + 1); if (!res) { kind = TK_OUTOFMEM; } else { strncpy (res,p,q - p); res[q - p] = '\0'; *token_val = res; } break; /* kind = TK_UNEXPCHAR; */ /* break; */ } return kind; } /* Gobble optional whitespace */ static void parse_whsp (const gchar **sp) { while (LDAP_SPACE (**sp)) (*sp)++; } /* Parse a sequence of dot-separated decimal strings */ static gchar * ldap_int_parse_numericoid (const gchar **sp, gint *code, const gint flags) { gchar *res = NULL; const gchar *start = *sp; gint len; gint quoted = 0; /* Netscape puts the SYNTAX value in quotes (incorrectly) */ if (flags & LDAP_SCHEMA_ALLOW_QUOTED && **sp == '\'') { quoted = 1; (*sp)++; start++; } /* Each iteration of this loop gets one decimal string */ while (**sp) { if (!LDAP_DIGIT (**sp)) { /* * Initial gchar is not a digit or gchar after dot is * not a digit */ *code = LDAP_SCHERR_NODIGIT; return NULL; } (*sp)++; while (LDAP_DIGIT (**sp)) (*sp)++; if (**sp != '.') break; /* Otherwise, gobble the dot and loop again */ (*sp)++; } /* Now *sp points at the gchar past the numericoid. Perfect. */ len = *sp - start; if (flags & LDAP_SCHEMA_ALLOW_QUOTED && quoted) { if (**sp == '\'') { (*sp)++; } else { *code = LDAP_SCHERR_UNEXPTOKEN; return NULL; } } if (flags & LDAP_SCHEMA_SKIP) { res = (gchar *) start; } else { res = LDAP_MALLOC (len + 1); if (!res) { *code = LDAP_SCHERR_OUTOFMEM; return (NULL); } strncpy (res,start,len); res[len] = '\0'; } return (res); } /* Parse a qdescr or a list of them enclosed in () */ static gchar ** parse_qdescrs (const gchar **sp, gint *code) { gchar ** res; gchar ** res1; gint kind; gchar *sval; gint size; gint pos; parse_whsp (sp); kind = get_token (sp,&sval); if (kind == TK_LEFTPAREN) { /* Let's presume there will be at least 2 entries */ size = 3; res = LDAP_CALLOC (3,sizeof (gchar *)); if (!res) { *code = LDAP_SCHERR_OUTOFMEM; return NULL; } pos = 0; while (1) { parse_whsp (sp); kind = get_token (sp,&sval); if (kind == TK_RIGHTPAREN) break; if (kind == TK_QDESCR) { if (pos == size - 2) { size++; res1 = LDAP_REALLOC (res,size *sizeof (gchar *)); if (!res1) { LDAP_VFREE (res); LDAP_FREE (sval); *code = LDAP_SCHERR_OUTOFMEM; return (NULL); } res = res1; } res[pos++] = sval; res[pos] = NULL; parse_whsp (sp); } else { LDAP_VFREE (res); LDAP_FREE (sval); *code = LDAP_SCHERR_UNEXPTOKEN; return (NULL); } } parse_whsp (sp); return (res); } else if (kind == TK_QDESCR) { res = LDAP_CALLOC (2,sizeof (gchar *)); if (!res) { *code = LDAP_SCHERR_OUTOFMEM; return NULL; } res[0] = sval; res[1] = NULL; parse_whsp (sp); return res; } else { LDAP_FREE (sval); *code = LDAP_SCHERR_BADNAME; return NULL; } } /* Parse a woid or a $-separated list of them enclosed in () */ static gchar ** parse_oids (const gchar **sp, gint *code, const gint allow_quoted) { gchar ** res; gchar ** res1; gint kind; gchar *sval; gint size; gint pos; /* * Strictly speaking, doing this here accepts whsp before the * ( at the begining of an oidlist, but this is harmless. Also, * we are very liberal in what we accept as an OID. Maybe * refine later. */ parse_whsp (sp); kind = get_token (sp,&sval); if (kind == TK_LEFTPAREN) { /* Let's presume there will be at least 2 entries */ size = 3; res = LDAP_CALLOC (3,sizeof (gchar *)); if (!res) { *code = LDAP_SCHERR_OUTOFMEM; return NULL; } pos = 0; parse_whsp (sp); kind = get_token (sp,&sval); if (kind == TK_BAREWORD || (allow_quoted && kind == TK_QDSTRING)) { res[pos++] = sval; res[pos] = NULL; } else { *code = LDAP_SCHERR_UNEXPTOKEN; LDAP_FREE (sval); LDAP_VFREE (res); return NULL; } parse_whsp (sp); while (1) { kind = get_token (sp,&sval); if (kind == TK_RIGHTPAREN) break; if (kind == TK_DOLLAR) { parse_whsp (sp); kind = get_token (sp,&sval); if (kind == TK_BAREWORD || (allow_quoted && kind == TK_QDSTRING)) { if (pos == size - 2) { size++; res1 = LDAP_REALLOC (res,size *sizeof (gchar *)); if (!res1) { LDAP_FREE (sval); LDAP_VFREE (res); *code = LDAP_SCHERR_OUTOFMEM; return (NULL); } res = res1; } res[pos++] = sval; res[pos] = NULL; } else { *code = LDAP_SCHERR_UNEXPTOKEN; LDAP_FREE (sval); LDAP_VFREE (res); return NULL; } parse_whsp (sp); } else { *code = LDAP_SCHERR_UNEXPTOKEN; LDAP_FREE (sval); LDAP_VFREE (res); return NULL; } } parse_whsp (sp); return (res); } else if (kind == TK_BAREWORD || (allow_quoted && kind == TK_QDSTRING)) { res = LDAP_CALLOC (2,sizeof (gchar *)); if (!res) { LDAP_FREE (sval); *code = LDAP_SCHERR_OUTOFMEM; return NULL; } res[0] = sval; res[1] = NULL; parse_whsp (sp); return res; } else { LDAP_FREE (sval); *code = LDAP_SCHERR_BADNAME; return NULL; } } static void ldap_objectclass_free (LDAPObjectClass *oc) { LDAP_FREE (oc->oc_oid); if (oc->oc_names) LDAP_VFREE (oc->oc_names); if (oc->oc_desc) LDAP_FREE (oc->oc_desc); if (oc->oc_sup_oids) LDAP_VFREE (oc->oc_sup_oids); if (oc->oc_at_oids_must) LDAP_VFREE (oc->oc_at_oids_must); if (oc->oc_at_oids_may) LDAP_VFREE (oc->oc_at_oids_may); LDAP_FREE (oc); } static LDAPObjectClass * ldap_str2objectclass (LDAP_CONST gchar *s, gint *code, LDAP_CONST gchar **errp, LDAP_CONST unsigned flags) { gint kind; const gchar *ss = s; gchar *sval; gint seen_name = 0; gint seen_desc = 0; gint seen_obsolete = 0; gint seen_sup = 0; gint seen_kind = 0; gint seen_must = 0; gint seen_may = 0; LDAPObjectClass *oc; gchar ** ext_vals; const gchar *savepos; if (!s) { *code = LDAP_SCHERR_EMPTY; *errp = ""; return NULL; } *errp = s; oc = LDAP_CALLOC (1,sizeof (LDAPObjectClass)); if (!oc) { *code = LDAP_SCHERR_OUTOFMEM; return NULL; } oc->oc_kind = LDAP_SCHEMA_STRUCTURAL; kind = get_token (&ss,&sval); if (kind != TK_LEFTPAREN) { *code = LDAP_SCHERR_NOLEFTPAREN; LDAP_FREE (sval); ldap_objectclass_free (oc); return NULL; } /* * Definitions MUST begin with an OID in the numericoid format. * However, this routine is used by clients to parse the response * from servers and very well known servers will provide an OID * in the wrong format or even no OID at all. We do our best to * extract info from those servers. */ parse_whsp (&ss); savepos = ss; oc->oc_oid = ldap_int_parse_numericoid (&ss,code,0); if (!oc->oc_oid) { if ((flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos)) { /* Backtracking */ ss = savepos; kind = get_token (&ss,&sval); if (kind == TK_BAREWORD) { if (!strcasecmp (sval, "NAME") || !strcasecmp (sval, "DESC") || !strcasecmp (sval, "OBSOLETE") || !strcasecmp (sval, "SUP") || !strcasecmp (sval, "ABSTRACT") || !strcasecmp (sval, "STRUCTURAL") || !strcasecmp (sval, "AUXILIARY") || !strcasecmp (sval, "MUST") || !strcasecmp (sval, "MAY") || !strncasecmp (sval, "X-", 2)) { /* Missing OID, backtrack */ ss = savepos; } else if (flags & LDAP_SCHEMA_ALLOW_OID_MACRO) { /* Non-numerical OID, ignore */ gint len = ss - savepos; oc->oc_oid = LDAP_MALLOC (len + 1); strncpy (oc->oc_oid, savepos, len); oc->oc_oid[len] = 0; } } LDAP_FREE (sval); } else { *errp = ss; ldap_objectclass_free (oc); return NULL; } } parse_whsp (&ss); /* * Beyond this point we will be liberal an accept the items * in any order. */ while (1) { kind = get_token (&ss,&sval); switch (kind) { case TK_EOS: *code = LDAP_SCHERR_NORIGHTPAREN; *errp = ss; ldap_objectclass_free (oc); return NULL; case TK_RIGHTPAREN: return oc; case TK_BAREWORD: if (!strcasecmp (sval,"NAME")) { LDAP_FREE (sval); if (seen_name) { *code = LDAP_SCHERR_DUPOPT; *errp = ss; ldap_objectclass_free (oc); return (NULL); } seen_name = 1; oc->oc_names = parse_qdescrs (&ss,code); if (!oc->oc_names) { if (*code != LDAP_SCHERR_OUTOFMEM) *code = LDAP_SCHERR_BADNAME; *errp = ss; ldap_objectclass_free (oc); return NULL; } } else if (!strcasecmp (sval,"DESC")) { LDAP_FREE (sval); if (seen_desc) { *code = LDAP_SCHERR_DUPOPT; *errp = ss; ldap_objectclass_free (oc); return (NULL); } seen_desc = 1; parse_whsp (&ss); kind = get_token (&ss,&sval); if (kind != TK_QDSTRING) { *code = LDAP_SCHERR_UNEXPTOKEN; *errp = ss; LDAP_FREE (sval); ldap_objectclass_free (oc); return NULL; } oc->oc_desc = sval; parse_whsp (&ss); } else if (!strcasecmp (sval,"OBSOLETE")) { LDAP_FREE (sval); if (seen_obsolete) { *code = LDAP_SCHERR_DUPOPT; *errp = ss; ldap_objectclass_free (oc); return (NULL); } seen_obsolete = 1; oc->oc_obsolete = LDAP_SCHEMA_YES; parse_whsp (&ss); } else if (!strcasecmp (sval,"SUP")) { LDAP_FREE (sval); if (seen_sup) { *code = LDAP_SCHERR_DUPOPT; *errp = ss; ldap_objectclass_free (oc); return (NULL); } seen_sup = 1; oc->oc_sup_oids = parse_oids (&ss, code, flags); if (!oc->oc_sup_oids) { *errp = ss; ldap_objectclass_free (oc); return NULL; } } else if (!strcasecmp (sval,"ABSTRACT")) { LDAP_FREE (sval); if (seen_kind) { *code = LDAP_SCHERR_DUPOPT; *errp = ss; ldap_objectclass_free (oc); return (NULL); } seen_kind = 1; oc->oc_kind = LDAP_SCHEMA_ABSTRACT; parse_whsp (&ss); } else if (!strcasecmp (sval,"STRUCTURAL")) { LDAP_FREE (sval); if (seen_kind) { *code = LDAP_SCHERR_DUPOPT; *errp = ss; ldap_objectclass_free (oc); return (NULL); } seen_kind = 1; oc->oc_kind = LDAP_SCHEMA_STRUCTURAL; parse_whsp (&ss); } else if (!strcasecmp (sval,"AUXILIARY")) { LDAP_FREE (sval); if (seen_kind) { *code = LDAP_SCHERR_DUPOPT; *errp = ss; ldap_objectclass_free (oc); return (NULL); } seen_kind = 1; oc->oc_kind = LDAP_SCHEMA_AUXILIARY; parse_whsp (&ss); } else if (!strcasecmp (sval,"MUST")) { LDAP_FREE (sval); if (seen_must) { *code = LDAP_SCHERR_DUPOPT; *errp = ss; ldap_objectclass_free (oc); return (NULL); } seen_must = 1; oc->oc_at_oids_must = parse_oids (&ss,code,0); if (!oc->oc_at_oids_must) { *errp = ss; ldap_objectclass_free (oc); return NULL; } parse_whsp (&ss); } else if (!strcasecmp (sval,"MAY")) { LDAP_FREE (sval); if (seen_may) { *code = LDAP_SCHERR_DUPOPT; *errp = ss; ldap_objectclass_free (oc); return (NULL); } seen_may = 1; oc->oc_at_oids_may = parse_oids (&ss,code,0); if (!oc->oc_at_oids_may) { *errp = ss; ldap_objectclass_free (oc); return NULL; } parse_whsp (&ss); } else if (sval[0] == 'X' && sval[1] == '-') { /* Should be parse_qdstrings */ ext_vals = parse_qdescrs (&ss, code); if (!ext_vals) { *errp = ss; ldap_objectclass_free (oc); return NULL; } #if 0 if (add_extension (&oc->oc_extensions, sval, ext_vals)) { *code = LDAP_SCHERR_OUTOFMEM; *errp = ss; LDAP_FREE (sval); ldap_objectclass_free (oc); return NULL; } #endif } else { *code = LDAP_SCHERR_UNEXPTOKEN; *errp = ss; LDAP_FREE (sval); ldap_objectclass_free (oc); return NULL; } break; default: *code = LDAP_SCHERR_UNEXPTOKEN; *errp = ss; LDAP_FREE (sval); ldap_objectclass_free (oc); return NULL; } } } /* from utf-8.c */ #define LDAP_UTF8_NEXT(p) g_utf8_next_char((p)) #define LDAP_UTF8_INCR(p) ((p)=LDAP_UTF8_NEXT((p))) #define ldap_x_utf8_to_ucs4(str) g_utf8_get_char(str) static gchar * ldap_utf8_strchr (const gchar *str, const gchar *chr) { for (; *str != '\0'; LDAP_UTF8_INCR (str)) { if (ldap_x_utf8_to_ucs4 (str) == ldap_x_utf8_to_ucs4 (chr)) { return (gchar *) str; } } return NULL; } static gsize ldap_utf8_strcspn (const gchar *str, const gchar *set) { const gchar *cstr; const gchar *cset; for (cstr = str; *cstr != '\0'; LDAP_UTF8_INCR (cstr)) { for (cset = set; *cset != '\0'; LDAP_UTF8_INCR (cset)) { if (ldap_x_utf8_to_ucs4 (cstr) == ldap_x_utf8_to_ucs4 (cset)) { return cstr - str; } } } return cstr - str; } static gsize ldap_utf8_strspn (const gchar *str, const gchar *set) { const gchar *cstr; const gchar *cset; for (cstr = str; *cstr != '\0'; LDAP_UTF8_INCR (cstr)) { for (cset = set; ; LDAP_UTF8_INCR (cset)) { if (*cset == '\0') { return cstr - str; } if (ldap_x_utf8_to_ucs4 (cstr) == ldap_x_utf8_to_ucs4 (cset)) { break; } } } return cstr - str; } static gchar *ldap_utf8_strtok (gchar *str, const gchar *sep, gchar **last) { gchar *begin; gchar *end; if (last == NULL) return NULL; begin = str ? str : *last; begin += ldap_utf8_strspn (begin, sep); if (*begin == '\0') { *last = NULL; return NULL; } end = &begin[ ldap_utf8_strcspn (begin, sep) ]; if (*end != '\0') { gchar *next = LDAP_UTF8_NEXT (end); *end = '\0'; end = next; } *last = end; return begin; } /* from ldap.h */ #define LDAP_URL_SUCCESS 0x00 /* Success */ #define LDAP_URL_ERR_MEM 0x01 /* can't allocate memory space */ #define LDAP_URL_ERR_PARAM 0x02 /* parameter is bad */ #define LDAP_URL_ERR_BADSCHEME 0x03 /* URL doesn't begin with "ldap[si]://" */ #define LDAP_URL_ERR_BADENCLOSURE 0x04 /* URL is missing trailing ">" */ #define LDAP_URL_ERR_BADURL 0x05 /* URL is bad */ #define LDAP_URL_ERR_BADHOST 0x06 /* host port is bad */ #define LDAP_URL_ERR_BADATTRS 0x07 /* bad (or missing) attributes */ #define LDAP_URL_ERR_BADSCOPE 0x08 /* scope string is invalid (or missing) */ #define LDAP_URL_ERR_BADFILTER 0x09 /* bad or missing filter */ #define LDAP_URL_ERR_BADEXTS 0x0a /* bad or missing extensions */ #define LDAP_URL_PREFIX "ldap://" #define LDAP_URL_PREFIX_LEN (sizeof(LDAP_URL_PREFIX)-1) #define LDAPS_URL_PREFIX "ldaps://" #define LDAPS_URL_PREFIX_LEN (sizeof(LDAPS_URL_PREFIX)-1) #define LDAPI_URL_PREFIX "ldapi://" #define LDAPI_URL_PREFIX_LEN (sizeof(LDAPI_URL_PREFIX)-1) #define LDAP_URL_URLCOLON "URL:" #define LDAP_URL_URLCOLON_LEN (sizeof(LDAP_URL_URLCOLON)-1) typedef struct ldap_url_desc { struct ldap_url_desc *lud_next; gchar *lud_scheme; gchar *lud_host; gint lud_port; gchar *lud_dn; gchar **lud_attrs; gint lud_scope; gchar *lud_filter; gchar **lud_exts; gint lud_crit_exts; } LDAPURLDesc; /* from url.c */ static const gchar * skip_url_prefix ( const gchar *url, gint *enclosedp, const gchar **scheme) { /* * return non-zero if this looks like a LDAP URL; zero if not * if non-zero returned, *urlp will be moved past "ldap://" part of URL */ const gchar *p; if (url == NULL) { return (NULL); } p = url; /* skip leading '<' (if any) */ if (*p == '<') { *enclosedp = 1; ++p; } else { *enclosedp = 0; } /* skip leading "URL:" (if any) */ if (strncasecmp (p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN) == 0) { p += LDAP_URL_URLCOLON_LEN; } /* check for "ldap://" prefix */ if (strncasecmp (p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN) == 0) { /* skip over "ldap://" prefix and return success */ p += LDAP_URL_PREFIX_LEN; *scheme = "ldap"; return (p); } /* check for "ldaps://" prefix */ if (strncasecmp (p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN) == 0) { /* skip over "ldaps://" prefix and return success */ p += LDAPS_URL_PREFIX_LEN; *scheme = "ldaps"; return (p); } /* check for "ldapi://" prefix */ if (strncasecmp (p, LDAPI_URL_PREFIX, LDAPI_URL_PREFIX_LEN) == 0) { /* skip over "ldapi://" prefix and return success */ p += LDAPI_URL_PREFIX_LEN; *scheme = "ldapi"; return (p); } #ifdef LDAP_CONNECTIONLESS /* check for "cldap://" prefix */ if (strncasecmp (p, LDAPC_URL_PREFIX, LDAPC_URL_PREFIX_LEN) == 0) { /* skip over "cldap://" prefix and return success */ p += LDAPC_URL_PREFIX_LEN; *scheme = "cldap"; return (p); } #endif return (NULL); } static gint str2scope (const gchar *p) { if (strcasecmp (p, "one") == 0) { return LDAP_SCOPE_ONELEVEL; } else if (strcasecmp (p, "onelevel") == 0) { return LDAP_SCOPE_ONELEVEL; } else if (strcasecmp (p, "base") == 0) { return LDAP_SCOPE_BASE; } else if (strcasecmp (p, "sub") == 0) { return LDAP_SCOPE_SUBTREE; } else if (strcasecmp (p, "subtree") == 0) { return LDAP_SCOPE_SUBTREE; } return (-1); } static void ldap_free_urldesc (LDAPURLDesc *ludp) { if (ludp == NULL) { return; } if (ludp->lud_scheme != NULL) { LDAP_FREE (ludp->lud_scheme); } if (ludp->lud_host != NULL) { LDAP_FREE (ludp->lud_host); } if (ludp->lud_dn != NULL) { LDAP_FREE (ludp->lud_dn); } if (ludp->lud_filter != NULL) { LDAP_FREE (ludp->lud_filter); } if (ludp->lud_attrs != NULL) { LDAP_VFREE (ludp->lud_attrs); } if (ludp->lud_exts != NULL) { LDAP_VFREE (ludp->lud_exts); } LDAP_FREE (ludp); } static gint ldap_int_unhex (gint c) { return (c >= '0' && c <= '9' ? c - '0' : c >= 'A' && c <= 'F' ? c - 'A' + 10 : c - 'a' + 10); } static void ldap_pvt_hex_unescape (gchar *s) { /* * Remove URL hex escapes from s... done in place. The basic concept for * this routine is borrowed from the WWW library HTUnEscape() routine. */ gchar *p; for (p = s; *s != '\0'; ++s) { if (*s == '%') { if (*++s == '\0') { break; } *p = ldap_int_unhex(*s) << 4; if (*++s == '\0') { break; } *p++ += ldap_int_unhex(*s); } else { *p++ = *s; } } *p = '\0'; } static gchar ** ldap_str2charray (const gchar *str_in, const gchar *brkstr) { gchar **res; gchar *str, *s; gchar *lasts; gint i; /* protect the input string from strtok */ str = LDAP_STRDUP (str_in); if (str == NULL) { return NULL; } i = 1; for (s = str; *s; s++) { if (ldap_utf8_strchr (brkstr, s) != NULL) { i++; } } res = (gchar **) LDAP_MALLOC ((i + 1) * sizeof (gchar *)); if (res == NULL) { LDAP_FREE (str); return NULL; } i = 0; for (s = ldap_utf8_strtok (str, brkstr, &lasts); s != NULL; s = ldap_utf8_strtok (NULL, brkstr, &lasts)) { res[i] = LDAP_STRDUP (s); if (res[i] == NULL) { for (--i; i >= 0; i--) { LDAP_FREE (res[i]); } LDAP_FREE (res); LDAP_FREE (str); return NULL; } i++; } res[i] = NULL; LDAP_FREE (str); return (res); } static gint ldap_url_parse_ext (LDAP_CONST gchar *url_in, LDAPURLDesc **ludpp) { /* * Pick apart the pieces of an LDAP URL. */ LDAPURLDesc *ludp; gchar *p, *q, *r; gint i, enclosed; const gchar *scheme = NULL; const gchar *url_tmp; gchar *url; if (url_in == NULL || ludpp == NULL) { return LDAP_URL_ERR_PARAM; } *ludpp = NULL; /* pessimistic */ url_tmp = skip_url_prefix (url_in, &enclosed, &scheme); if (url_tmp == NULL) { return LDAP_URL_ERR_BADSCHEME; } assert (scheme); /* make working copy of the remainder of the URL */ url = LDAP_STRDUP (url_tmp); if (url == NULL) { return LDAP_URL_ERR_MEM; } if (enclosed) { p = &url[strlen (url) - 1]; if (*p != '>') { LDAP_FREE (url); return LDAP_URL_ERR_BADENCLOSURE; } *p = '\0'; } /* allocate return struct */ ludp = (LDAPURLDesc *) LDAP_CALLOC (1, sizeof (LDAPURLDesc)); if (ludp == NULL) { LDAP_FREE (url); return LDAP_URL_ERR_MEM; } ludp->lud_next = NULL; ludp->lud_host = NULL; ludp->lud_port = 0; ludp->lud_dn = NULL; ludp->lud_attrs = NULL; ludp->lud_filter = NULL; ludp->lud_scope = LDAP_SCOPE_DEFAULT; ludp->lud_filter = NULL; ludp->lud_exts = NULL; ludp->lud_scheme = LDAP_STRDUP (scheme); if (ludp->lud_scheme == NULL) { LDAP_FREE (url); ldap_free_urldesc (ludp); return LDAP_URL_ERR_MEM; } /* scan forward for '/' that marks end of hostport and begin. of dn */ p = strchr (url, '/'); if (p != NULL) { /* terminate hostport; point to start of dn */ *p++ = '\0'; } /* IPv6 syntax with [ip address]:port */ if (*url == '[') { r = strchr (url, ']'); if (r == NULL) { LDAP_FREE (url); ldap_free_urldesc (ludp); return LDAP_URL_ERR_BADURL; } *r++ = '\0'; q = strchr (r, ':'); } else { q = strchr (url, ':'); } if (q != NULL) { gchar *next; *q++ = '\0'; ldap_pvt_hex_unescape (q); if (*q == '\0') { LDAP_FREE (url); ldap_free_urldesc (ludp); return LDAP_URL_ERR_BADURL; } ludp->lud_port = strtol (q, &next, 10); if (next == NULL || next[0] != '\0') { LDAP_FREE (url); ldap_free_urldesc (ludp); return LDAP_URL_ERR_BADURL; } } ldap_pvt_hex_unescape (url); /* If [ip address]:port syntax, url is [ip and we skip the [ */ ludp->lud_host = LDAP_STRDUP (url + (*url == '[')); if (ludp->lud_host == NULL) { LDAP_FREE (url); ldap_free_urldesc (ludp); return LDAP_URL_ERR_MEM; } /* * Kludge. ldap://111.222.333.444:389??cn=abc,o=company * * On early Novell releases, search references/referrals were returned * in this format, i.e., the dn was kind of in the scope position, * but the required slash is missing. The whole thing is illegal syntax, * but we need to account for it. Fortunately it can't be confused with * anything real. */ if ((p == NULL) && (q != NULL) && ((q = strchr (q, '?')) != NULL)) { q++; /* ? immediately followed by question */ if (*q == '?') { q++; if (*q != '\0') { /* parse dn part */ ldap_pvt_hex_unescape (q); ludp->lud_dn = LDAP_STRDUP (q); } else { ludp->lud_dn = LDAP_STRDUP (""); } if (ludp->lud_dn == NULL) { LDAP_FREE (url); ldap_free_urldesc (ludp); return LDAP_URL_ERR_MEM; } } } if (p == NULL) { LDAP_FREE (url); *ludpp = ludp; return LDAP_URL_SUCCESS; } /* scan forward for '?' that may marks end of dn */ q = strchr (p, '?'); if (q != NULL) { /* terminate dn part */ *q++ = '\0'; } if (*p != '\0') { /* parse dn part */ ldap_pvt_hex_unescape (p); ludp->lud_dn = LDAP_STRDUP (p); } else { ludp->lud_dn = LDAP_STRDUP (""); } if (ludp->lud_dn == NULL) { LDAP_FREE (url); ldap_free_urldesc (ludp); return LDAP_URL_ERR_MEM; } if (q == NULL) { /* no more */ LDAP_FREE (url); *ludpp = ludp; return LDAP_URL_SUCCESS; } /* scan forward for '?' that may marks end of attributes */ p = q; q = strchr (p, '?'); if (q != NULL) { /* terminate attributes part */ *q++ = '\0'; } if (*p != '\0') { /* parse attributes */ ldap_pvt_hex_unescape (p); ludp->lud_attrs = ldap_str2charray (p, ","); if (ludp->lud_attrs == NULL) { LDAP_FREE (url); ldap_free_urldesc (ludp); return LDAP_URL_ERR_BADATTRS; } } if (q == NULL) { /* no more */ LDAP_FREE (url); *ludpp = ludp; return LDAP_URL_SUCCESS; } /* scan forward for '?' that may marks end of scope */ p = q; q = strchr (p, '?'); if (q != NULL) { /* terminate the scope part */ *q++ = '\0'; } if (*p != '\0') { /* parse the scope */ ldap_pvt_hex_unescape (p); ludp->lud_scope = str2scope (p); if (ludp->lud_scope == -1) { LDAP_FREE (url); ldap_free_urldesc (ludp); return LDAP_URL_ERR_BADSCOPE; } } if (q == NULL) { /* no more */ LDAP_FREE (url); *ludpp = ludp; return LDAP_URL_SUCCESS; } /* scan forward for '?' that may marks end of filter */ p = q; q = strchr (p, '?'); if (q != NULL) { /* terminate the filter part */ *q++ = '\0'; } if (*p != '\0') { /* parse the filter */ ldap_pvt_hex_unescape (p); if (!*p) { /* missing filter */ LDAP_FREE (url); ldap_free_urldesc (ludp); return LDAP_URL_ERR_BADFILTER; } LDAP_FREE (ludp->lud_filter); ludp->lud_filter = LDAP_STRDUP (p); if (ludp->lud_filter == NULL) { LDAP_FREE (url); ldap_free_urldesc (ludp); return LDAP_URL_ERR_MEM; } } if (q == NULL) { /* no more */ LDAP_FREE (url); *ludpp = ludp; return LDAP_URL_SUCCESS; } /* scan forward for '?' that may marks end of extensions */ p = q; q = strchr (p, '?'); if (q != NULL) { /* extra '?' */ LDAP_FREE (url); ldap_free_urldesc (ludp); return LDAP_URL_ERR_BADURL; } /* parse the extensions */ ludp->lud_exts = ldap_str2charray (p, ","); if (ludp->lud_exts == NULL) { LDAP_FREE (url); ldap_free_urldesc (ludp); return LDAP_URL_ERR_BADEXTS; } for (i = 0; ludp->lud_exts[i] != NULL; i++) { ldap_pvt_hex_unescape (ludp->lud_exts[i]); if (*ludp->lud_exts[i] == '!') { /* count the number of critical extensions */ ludp->lud_crit_exts++; } } if (i == 0) { /* must have 1 or more */ LDAP_FREE (url); ldap_free_urldesc (ludp); return LDAP_URL_ERR_BADEXTS; } /* no more */ *ludpp = ludp; LDAP_FREE (url); return LDAP_URL_SUCCESS; } static gint ldap_url_parse (LDAP_CONST gchar *url_in, LDAPURLDesc **ludpp) { gint rc = ldap_url_parse_ext (url_in, ludpp); if (rc != LDAP_URL_SUCCESS) { return rc; } if ((*ludpp)->lud_scope == LDAP_SCOPE_DEFAULT) { (*ludpp)->lud_scope = LDAP_SCOPE_BASE; } if ((*ludpp)->lud_host != NULL && *(*ludpp)->lud_host == '\0') { LDAP_FREE ((*ludpp)->lud_host); (*ludpp)->lud_host = NULL; } if ((*ludpp)->lud_port == 0) { if (strcmp ((*ludpp)->lud_scheme, "ldap") == 0) { (*ludpp)->lud_port = LDAP_PORT; #ifdef LDAP_CONNECTIONLESS } else if (strcmp ((*ludpp)->lud_scheme, "cldap") == 0) { (*ludpp)->lud_port = LDAP_PORT; #endif } else if (strcmp ((*ludpp)->lud_scheme, "ldaps") == 0) { (*ludpp)->lud_port = LDAPS_PORT; } } return rc; }