1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Directory lookup functions. These are shims that translate from the API 29 * into the RPC protocol. 30 */ 31 32 #include <assert.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <stdarg.h> 36 #include <malloc.h> 37 #include <sys/types.h> 38 #include <netdb.h> 39 #include <pthread.h> 40 #include <unistd.h> 41 #include <string.h> 42 #include "directory.h" 43 #include "directory_private.h" 44 #include <rpcsvc/idmap_prot.h> 45 #include "directory_library_impl.h" 46 #include "sized_array.h" 47 48 static directory_error_t copy_directory_attribute_value( 49 directory_attribute_value_t *dav, 50 directory_values_rpc *dav_rpc); 51 static directory_error_t copy_directory_entry(directory_entry_t *ent, 52 directory_entry_rpc *ent_rpc); 53 static void directory_results_free(directory_results_rpc *dr); 54 static directory_datum_t directory_datum(void *data, size_t len); 55 static void directory_datum_free(directory_datum_t d); 56 57 /* 58 * This is the actual implementation of the opaque directory_t structure. 59 */ 60 struct directory { 61 CLIENT *client; 62 }; 63 64 /* 65 * Set up a directory search context. 66 */ 67 directory_error_t 68 directory_open(directory_t *ret) 69 { 70 directory_t d; 71 directory_error_t de; 72 char host[] = "localhost"; 73 74 *ret = NULL; 75 76 d = calloc(1, sizeof (*d)); 77 if (d == NULL) 78 goto nomem; 79 80 d->client = clnt_door_create(IDMAP_PROG, IDMAP_V1, 0); 81 if (d->client == NULL) { 82 de = directory_error("clnt_create.directory_open", 83 "Error: %1", 84 clnt_spcreateerror(host), 85 NULL); 86 goto err; 87 } 88 89 *ret = d; 90 return (NULL); 91 92 nomem: 93 de = directory_error("ENOMEM.directory_open", 94 "Insufficient memory setting up directory access", NULL); 95 err: 96 directory_close(d); 97 return (de); 98 } 99 100 /* 101 * Tear down a directory search context. 102 * 103 * Does nothing if d==NULL. 104 */ 105 void 106 directory_close(directory_t d) 107 { 108 if (d == NULL) 109 return; 110 111 if (d->client != NULL) 112 clnt_destroy(d->client); 113 114 free(d); 115 } 116 117 /* 118 * Given a list of identifiers, a list of their types, and a list of attributes, 119 * return the information. 120 */ 121 directory_error_t 122 directory_get_v( 123 directory_t d, 124 directory_entry_list_t *ret, 125 char **ids, 126 int nids, 127 char *types, 128 char **attr_list) 129 { 130 int nattrs; 131 directory_entry_list_t del; 132 directory_error_t de; 133 directory_results_rpc dr; 134 idmap_utf8str_list sl_ids; 135 idmap_utf8str_list sl_attrs; 136 directory_entry_rpc *users; 137 int i; 138 enum clnt_stat cs; 139 140 *ret = NULL; 141 del = NULL; 142 143 if (nids == 0) { 144 for (nids = 0; ids[nids] != NULL; nids++) 145 /* LOOP */; 146 } 147 148 for (nattrs = 0; attr_list[nattrs] != NULL; nattrs++) 149 /* LOOP */; 150 151 sl_ids.idmap_utf8str_list_len = nids; 152 sl_ids.idmap_utf8str_list_val = ids; 153 sl_attrs.idmap_utf8str_list_len = nattrs; 154 sl_attrs.idmap_utf8str_list_val = attr_list; 155 156 (void) memset(&dr, 0, sizeof (dr)); 157 cs = directory_get_common_1(sl_ids, types, sl_attrs, &dr, d->client); 158 if (cs != RPC_SUCCESS) { 159 char errbuf[100]; /* well long enough for any integer */ 160 (void) sprintf(errbuf, "%d", cs); 161 de = directory_error("RPC.Get_common", 162 "Get_common RPC (%1)%2", errbuf, 163 clnt_sperror(d->client, ""), NULL); 164 goto err; 165 } 166 167 if (dr.failed) { 168 de = directory_error_from_rpc( 169 &dr.directory_results_rpc_u.err); 170 goto err; 171 } 172 173 assert(dr.directory_results_rpc_u.entries.entries_len == nids); 174 175 users = dr.directory_results_rpc_u.entries.entries_val; 176 177 del = sized_array(nids, sizeof (directory_entry_t)); 178 179 for (i = 0; i < nids; i++) { 180 de = copy_directory_entry(&del[i], &users[i]); 181 if (de != NULL) 182 goto err; 183 } 184 185 directory_results_free(&dr); 186 187 *ret = del; 188 return (NULL); 189 190 err: 191 directory_results_free(&dr); 192 directory_free(del); 193 return (de); 194 } 195 196 /* 197 * Free the results from a directory_get_*() request. 198 */ 199 void 200 directory_free(directory_entry_list_t del) 201 { 202 directory_entry_t *ent; 203 directory_attribute_value_t dav; 204 int i; 205 int j; 206 int k; 207 208 if (del == NULL) 209 return; 210 211 /* For each directory entry returned */ 212 for (i = 0; i < sized_array_n(del); i++) { 213 ent = &del[i]; 214 215 if (ent->attrs != NULL) { 216 /* For each attribute */ 217 for (j = 0; j < sized_array_n(ent->attrs); j++) { 218 dav = ent->attrs[j]; 219 if (dav != NULL) { 220 for (k = 0; k < sized_array_n(dav); k++) 221 directory_datum_free(dav[k]); 222 223 sized_array_free(dav); 224 } 225 } 226 sized_array_free(ent->attrs); 227 } 228 229 directory_error_free(ent->err); 230 } 231 232 sized_array_free(del); 233 } 234 235 /* 236 * Create a directory datum. Note that we allocate an extra byte and 237 * zero it, so that strings get null-terminated. Return NULL on error. 238 */ 239 static 240 directory_datum_t 241 directory_datum(void *data, size_t len) 242 { 243 void *p; 244 245 p = sized_array(len + 1, 1); 246 if (p == NULL) 247 return (NULL); 248 (void) memcpy(p, data, len); 249 *((char *)p + len) = '\0'; 250 return (p); 251 } 252 253 /* 254 * Return the size of a directory_datum_t. Note that this does not include 255 * the terminating \0, so it represents the value as returned by LDAP. 256 */ 257 size_t 258 directory_datum_len(directory_datum_t d) 259 { 260 /* 261 * Deduct the terminal \0, so that binary data gets the 262 * expected length. 263 */ 264 return (sized_array_n(d) - 1); 265 } 266 267 static 268 void 269 directory_datum_free(directory_datum_t d) 270 { 271 sized_array_free(d); 272 } 273 274 /* 275 * Unmarshall an RPC directory entry into an API directory entry. 276 */ 277 static 278 directory_error_t 279 copy_directory_entry( 280 directory_entry_t *ent, 281 directory_entry_rpc *ent_rpc) 282 { 283 int nattrs; 284 int i; 285 directory_error_t de; 286 287 /* If the entry wasn't found, leave the entry attrs and err NULL. */ 288 if (ent_rpc->status == DIRECTORY_NOT_FOUND) 289 return (NULL); 290 291 if (ent_rpc->status == DIRECTORY_ERROR) { 292 ent->err = directory_error_from_rpc( 293 &ent_rpc->directory_entry_rpc_u.err); 294 return (NULL); 295 } 296 297 nattrs = ent_rpc->directory_entry_rpc_u.attrs.attrs_len; 298 299 ent->attrs = sized_array(nattrs, sizeof (directory_attribute_value_t)); 300 if (ent->attrs == NULL) { 301 return (directory_error("ENOMEM.copy_directory_entry", 302 "Insufficient memory copying directory entry", NULL)); 303 } 304 for (i = 0; i < nattrs; i++) { 305 de = copy_directory_attribute_value(&ent->attrs[i], 306 &ent_rpc->directory_entry_rpc_u.attrs.attrs_val[i]); 307 if (de != NULL) 308 return (de); 309 } 310 311 return (NULL); 312 } 313 314 /* 315 * Unmarshall an RPC directory attribute value into the API equivalent. 316 * 317 * Note that on error some entries may have been copied, and so 318 * the caller needs to clean up dav. This is normally not a problem 319 * since the caller will have called this function several times and 320 * will need to clean up the results from the other calls too. 321 */ 322 static 323 directory_error_t 324 copy_directory_attribute_value( 325 directory_attribute_value_t *dav, 326 directory_values_rpc *dav_rpc) 327 { 328 int i; 329 int nvals; 330 directory_value_rpc *vals; 331 332 /* If it wasn't found, leave the corresponding entry NULL */ 333 if (!dav_rpc->found) 334 return (NULL); 335 336 nvals = dav_rpc->directory_values_rpc_u.values.values_len; 337 *dav = sized_array(nvals + 1, sizeof (directory_datum_t)); 338 if (*dav == NULL) { 339 return (directory_error("ENOMEM.copy_directory_attribute_value", 340 "Insufficient memory copying directory entry", NULL)); 341 } 342 343 vals = dav_rpc->directory_values_rpc_u.values.values_val; 344 for (i = 0; i < nvals; i++) { 345 (*dav)[i] = directory_datum(vals[i].directory_value_rpc_val, 346 vals[i].directory_value_rpc_len); 347 if ((*dav)[i] == NULL) { 348 return (directory_error( 349 "ENOMEM.copy_directory_attribute_value", 350 "Insufficient memory copying directory entry", 351 NULL)); 352 } 353 } 354 355 return (NULL); 356 } 357 358 /* 359 * Free the results of a directory RPC request. 360 */ 361 static 362 void 363 directory_results_free(directory_results_rpc *dr) 364 { 365 xdr_free(xdr_directory_results_rpc, (char *)&dr); 366 } 367