1/* 2 ldb database library 3 4 Copyright (C) Andrew Tridgell 2005 5 6 ** NOTE! The following LGPL license applies to the ldb 7 ** library. This does NOT imply that all of Samba is released 8 ** under the LGPL 9 10 This library is free software; you can redistribute it and/or 11 modify it under the terms of the GNU Lesser General Public 12 License as published by the Free Software Foundation; either 13 version 3 of the License, or (at your option) any later version. 14 15 This library is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 Lesser General Public License for more details. 19 20 You should have received a copy of the GNU Lesser General Public 21 License along with this library; if not, see <http://www.gnu.org/licenses/>. 22*/ 23/* 24 attribute handlers for well known attribute types, selected by syntax OID 25 see rfc2252 26*/ 27 28#include "ldb_private.h" 29#include "system/locale.h" 30#include "ldb_handlers.h" 31 32/* 33 default handler that just copies a ldb_val. 34*/ 35int ldb_handler_copy(struct ldb_context *ldb, void *mem_ctx, 36 const struct ldb_val *in, struct ldb_val *out) 37{ 38 *out = ldb_val_dup(mem_ctx, in); 39 if (in->length > 0 && out->data == NULL) { 40 ldb_oom(ldb); 41 return -1; 42 } 43 return 0; 44} 45 46/* 47 a case folding copy handler, removing leading and trailing spaces and 48 multiple internal spaces 49 50 We exploit the fact that utf8 never uses the space octet except for 51 the space itself 52*/ 53int ldb_handler_fold(struct ldb_context *ldb, void *mem_ctx, 54 const struct ldb_val *in, struct ldb_val *out) 55{ 56 char *s, *t; 57 int l; 58 59 if (!in || !out || !(in->data)) { 60 return -1; 61 } 62 63 out->data = (uint8_t *)ldb_casefold(ldb, mem_ctx, (const char *)(in->data), in->length); 64 if (out->data == NULL) { 65 ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_handler_fold: unable to casefold string [%.*s]", (int)in->length, (const char *)in->data); 66 return -1; 67 } 68 69 s = (char *)(out->data); 70 71 /* remove trailing spaces if any */ 72 l = strlen(s); 73 while (l > 0 && s[l - 1] == ' ') l--; 74 s[l] = '\0'; 75 76 /* remove leading spaces if any */ 77 if (*s == ' ') { 78 for (t = s; *s == ' '; s++) ; 79 80 /* remove leading spaces by moving down the string */ 81 memmove(t, s, l); 82 83 s = t; 84 } 85 86 /* check middle spaces */ 87 while ((t = strchr(s, ' ')) != NULL) { 88 for (s = t; *s == ' '; s++) ; 89 90 if ((s - t) > 1) { 91 l = strlen(s); 92 93 /* remove all spaces but one by moving down the string */ 94 memmove(t + 1, s, l); 95 } 96 } 97 98 out->length = strlen((char *)out->data); 99 return 0; 100} 101 102 103 104/* 105 canonicalise a ldap Integer 106 rfc2252 specifies it should be in decimal form 107*/ 108static int ldb_canonicalise_Integer(struct ldb_context *ldb, void *mem_ctx, 109 const struct ldb_val *in, struct ldb_val *out) 110{ 111 char *end; 112 long long i = strtoll((char *)in->data, &end, 0); 113 if (*end != 0) { 114 return -1; 115 } 116 out->data = (uint8_t *)talloc_asprintf(mem_ctx, "%lld", i); 117 if (out->data == NULL) { 118 return -1; 119 } 120 out->length = strlen((char *)out->data); 121 return 0; 122} 123 124/* 125 compare two Integers 126*/ 127static int ldb_comparison_Integer(struct ldb_context *ldb, void *mem_ctx, 128 const struct ldb_val *v1, const struct ldb_val *v2) 129{ 130 return strtoll((char *)v1->data, NULL, 0) - strtoll((char *)v2->data, NULL, 0); 131} 132 133/* 134 canonicalise a ldap Boolean 135 rfc2252 specifies it should be either "TRUE" or "FALSE" 136*/ 137static int ldb_canonicalise_Boolean(struct ldb_context *ldb, void *mem_ctx, 138 const struct ldb_val *in, struct ldb_val *out) 139{ 140 if (strncasecmp((char *)in->data, "TRUE", in->length) == 0) { 141 out->data = (uint8_t *)talloc_strdup(mem_ctx, "TRUE"); 142 out->length = 4; 143 } else if (strncasecmp((char *)in->data, "FALSE", in->length) == 0) { 144 out->data = (uint8_t *)talloc_strdup(mem_ctx, "FALSE"); 145 out->length = 4; 146 } else { 147 return -1; 148 } 149 return 0; 150} 151 152/* 153 compare two Booleans 154*/ 155static int ldb_comparison_Boolean(struct ldb_context *ldb, void *mem_ctx, 156 const struct ldb_val *v1, const struct ldb_val *v2) 157{ 158 if (v1->length != v2->length) { 159 return v1->length - v2->length; 160 } 161 return strncasecmp((char *)v1->data, (char *)v2->data, v1->length); 162} 163 164 165/* 166 compare two binary blobs 167*/ 168int ldb_comparison_binary(struct ldb_context *ldb, void *mem_ctx, 169 const struct ldb_val *v1, const struct ldb_val *v2) 170{ 171 if (v1->length != v2->length) { 172 return v1->length - v2->length; 173 } 174 return memcmp(v1->data, v2->data, v1->length); 175} 176 177/* 178 compare two case insensitive strings, ignoring multiple whitespaces 179 and leading and trailing whitespaces 180 see rfc2252 section 8.1 181 182 try to optimize for the ascii case, 183 but if we find out an utf8 codepoint revert to slower but correct function 184*/ 185int ldb_comparison_fold(struct ldb_context *ldb, void *mem_ctx, 186 const struct ldb_val *v1, const struct ldb_val *v2) 187{ 188 const char *s1=(const char *)v1->data, *s2=(const char *)v2->data; 189 size_t n1 = v1->length, n2 = v2->length; 190 char *b1, *b2; 191 const char *u1, *u2; 192 int ret; 193 while (n1 && *s1 == ' ') { s1++; n1--; }; 194 while (n2 && *s2 == ' ') { s2++; n2--; }; 195 196 while (n1 && n2 && *s1 && *s2) { 197 /* the first 127 (0x7F) chars are ascii and utf8 guarantes they 198 * never appear in multibyte sequences */ 199 if (((unsigned char)s1[0]) & 0x80) goto utf8str; 200 if (((unsigned char)s2[0]) & 0x80) goto utf8str; 201 if (toupper((unsigned char)*s1) != toupper((unsigned char)*s2)) 202 break; 203 if (*s1 == ' ') { 204 while (n1 && s1[0] == s1[1]) { s1++; n1--; } 205 while (n2 && s2[0] == s2[1]) { s2++; n2--; } 206 } 207 s1++; s2++; 208 n1--; n2--; 209 } 210 211 /* check for trailing spaces only if the other pointers has 212 * reached the end of the strings otherwise we can 213 * mistakenly match. ex. "domain users" <-> 214 * "domainUpdates" 215 */ 216 if (n1 && *s1 == ' ' && (!n2 || !*s2)) { 217 while (n1 && *s1 == ' ') { s1++; n1--; } 218 } 219 if (n2 && *s2 == ' ' && (!n1 || !*s1)) { 220 while (n2 && *s2 == ' ') { s2++; n2--; } 221 } 222 if (n1 == 0 && n2 != 0) { 223 return -(int)toupper(*s2); 224 } 225 if (n2 == 0 && n1 != 0) { 226 return (int)toupper(*s1); 227 } 228 if (n2 == 0 && n2 == 0) { 229 return 0; 230 } 231 return (int)toupper(*s1) - (int)toupper(*s2); 232 233utf8str: 234 /* no need to recheck from the start, just from the first utf8 char found */ 235 b1 = ldb_casefold(ldb, mem_ctx, s1, n1); 236 b2 = ldb_casefold(ldb, mem_ctx, s2, n2); 237 238 if (!b1 || !b2) { 239 /* One of the strings was not UTF8, so we have no 240 * options but to do a binary compare */ 241 talloc_free(b1); 242 talloc_free(b2); 243 if (memcmp(s1, s2, MIN(n1, n2)) == 0) { 244 if (n1 == n2) return 0; 245 if (n1 > n2) { 246 return (int)toupper(s1[n2]); 247 } else { 248 return -(int)toupper(s2[n1]); 249 } 250 } 251 } 252 253 u1 = b1; 254 u2 = b2; 255 256 while (*u1 & *u2) { 257 if (*u1 != *u2) 258 break; 259 if (*u1 == ' ') { 260 while (u1[0] == u1[1]) u1++; 261 while (u2[0] == u2[1]) u2++; 262 } 263 u1++; u2++; 264 } 265 if (! (*u1 && *u2)) { 266 while (*u1 == ' ') u1++; 267 while (*u2 == ' ') u2++; 268 } 269 ret = (int)(*u1 - *u2); 270 271 talloc_free(b1); 272 talloc_free(b2); 273 274 return ret; 275} 276 277 278/* 279 canonicalise a attribute in DN format 280*/ 281static int ldb_canonicalise_dn(struct ldb_context *ldb, void *mem_ctx, 282 const struct ldb_val *in, struct ldb_val *out) 283{ 284 struct ldb_dn *dn; 285 int ret = -1; 286 287 out->length = 0; 288 out->data = NULL; 289 290 dn = ldb_dn_from_ldb_val(mem_ctx, ldb, in); 291 if ( ! ldb_dn_validate(dn)) { 292 return LDB_ERR_INVALID_DN_SYNTAX; 293 } 294 295 out->data = (uint8_t *)ldb_dn_alloc_casefold(mem_ctx, dn); 296 if (out->data == NULL) { 297 goto done; 298 } 299 out->length = strlen((char *)out->data); 300 301 ret = 0; 302 303done: 304 talloc_free(dn); 305 306 return ret; 307} 308 309/* 310 compare two dns 311*/ 312static int ldb_comparison_dn(struct ldb_context *ldb, void *mem_ctx, 313 const struct ldb_val *v1, const struct ldb_val *v2) 314{ 315 struct ldb_dn *dn1 = NULL, *dn2 = NULL; 316 int ret; 317 318 dn1 = ldb_dn_from_ldb_val(mem_ctx, ldb, v1); 319 if ( ! ldb_dn_validate(dn1)) return -1; 320 321 dn2 = ldb_dn_from_ldb_val(mem_ctx, ldb, v2); 322 if ( ! ldb_dn_validate(dn2)) { 323 talloc_free(dn1); 324 return -1; 325 } 326 327 ret = ldb_dn_compare(dn1, dn2); 328 329 talloc_free(dn1); 330 talloc_free(dn2); 331 return ret; 332} 333 334/* 335 compare two utc time values. 1 second resolution 336*/ 337static int ldb_comparison_utctime(struct ldb_context *ldb, void *mem_ctx, 338 const struct ldb_val *v1, const struct ldb_val *v2) 339{ 340 time_t t1, t2; 341 t1 = ldb_string_to_time((char *)v1->data); 342 t2 = ldb_string_to_time((char *)v2->data); 343 return (int)t2 - (int)t1; 344} 345 346/* 347 canonicalise a utc time 348*/ 349static int ldb_canonicalise_utctime(struct ldb_context *ldb, void *mem_ctx, 350 const struct ldb_val *in, struct ldb_val *out) 351{ 352 time_t t = ldb_string_to_time((char *)in->data); 353 out->data = (uint8_t *)ldb_timestring(mem_ctx, t); 354 if (out->data == NULL) { 355 return -1; 356 } 357 out->length = strlen((char *)out->data); 358 return 0; 359} 360 361/* 362 table of standard attribute handlers 363*/ 364static const struct ldb_schema_syntax ldb_standard_syntaxes[] = { 365 { 366 .name = LDB_SYNTAX_INTEGER, 367 .ldif_read_fn = ldb_handler_copy, 368 .ldif_write_fn = ldb_handler_copy, 369 .canonicalise_fn = ldb_canonicalise_Integer, 370 .comparison_fn = ldb_comparison_Integer 371 }, 372 { 373 .name = LDB_SYNTAX_OCTET_STRING, 374 .ldif_read_fn = ldb_handler_copy, 375 .ldif_write_fn = ldb_handler_copy, 376 .canonicalise_fn = ldb_handler_copy, 377 .comparison_fn = ldb_comparison_binary 378 }, 379 { 380 .name = LDB_SYNTAX_DIRECTORY_STRING, 381 .ldif_read_fn = ldb_handler_copy, 382 .ldif_write_fn = ldb_handler_copy, 383 .canonicalise_fn = ldb_handler_fold, 384 .comparison_fn = ldb_comparison_fold 385 }, 386 { 387 .name = LDB_SYNTAX_DN, 388 .ldif_read_fn = ldb_handler_copy, 389 .ldif_write_fn = ldb_handler_copy, 390 .canonicalise_fn = ldb_canonicalise_dn, 391 .comparison_fn = ldb_comparison_dn 392 }, 393 { 394 .name = LDB_SYNTAX_OBJECTCLASS, 395 .ldif_read_fn = ldb_handler_copy, 396 .ldif_write_fn = ldb_handler_copy, 397 .canonicalise_fn = ldb_handler_fold, 398 .comparison_fn = ldb_comparison_fold 399 }, 400 { 401 .name = LDB_SYNTAX_UTC_TIME, 402 .ldif_read_fn = ldb_handler_copy, 403 .ldif_write_fn = ldb_handler_copy, 404 .canonicalise_fn = ldb_canonicalise_utctime, 405 .comparison_fn = ldb_comparison_utctime 406 }, 407 { 408 .name = LDB_SYNTAX_BOOLEAN, 409 .ldif_read_fn = ldb_handler_copy, 410 .ldif_write_fn = ldb_handler_copy, 411 .canonicalise_fn = ldb_canonicalise_Boolean, 412 .comparison_fn = ldb_comparison_Boolean 413 }, 414}; 415 416 417/* 418 return the attribute handlers for a given syntax name 419*/ 420const struct ldb_schema_syntax *ldb_standard_syntax_by_name(struct ldb_context *ldb, 421 const char *syntax) 422{ 423 int i; 424 unsigned num_handlers = sizeof(ldb_standard_syntaxes)/sizeof(ldb_standard_syntaxes[0]); 425 /* TODO: should be replaced with a binary search */ 426 for (i=0;i<num_handlers;i++) { 427 if (strcmp(ldb_standard_syntaxes[i].name, syntax) == 0) { 428 return &ldb_standard_syntaxes[i]; 429 } 430 } 431 return NULL; 432} 433