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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * Retrieve directory information for built-in users and groups 28 */ 29 30 #include <stdio.h> 31 #include <limits.h> 32 #include <sys/idmap.h> 33 #include <sys/param.h> 34 #include <string.h> 35 #include <stdlib.h> 36 #include <netdb.h> 37 #include <libuutil.h> 38 #include <note.h> 39 #include "idmapd.h" 40 #include "directory.h" 41 #include "directory_private.h" 42 #include <rpcsvc/idmap_prot.h> 43 #include "directory_server_impl.h" 44 #include "sidutil.h" 45 46 static directory_error_t sid_dav(directory_values_rpc *lvals, 47 const wksids_table_t *wksid); 48 static directory_error_t directory_provider_builtin_populate( 49 directory_entry_rpc *pent, const wksids_table_t *wksid, 50 idmap_utf8str_list *attrs); 51 52 /* 53 * Retrieve information by name. 54 * Called indirectly through the directory_provider_static structure. 55 */ 56 static 57 directory_error_t 58 directory_provider_builtin_get( 59 directory_entry_rpc *del, 60 idmap_utf8str_list *ids, 61 idmap_utf8str types, 62 idmap_utf8str_list *attrs) 63 { 64 int i; 65 66 for (i = 0; i < ids->idmap_utf8str_list_len; i++) { 67 const wksids_table_t *wksid; 68 directory_error_t de; 69 int type; 70 71 /* 72 * Extract the type for this particular ID. 73 * Advance to the next type, if it's there, else keep 74 * using this type until we run out of IDs. 75 */ 76 type = *types; 77 if (*(types+1) != '\0') 78 types++; 79 80 /* 81 * If this entry has already been handled, one way or another, 82 * skip it. 83 */ 84 if (del[i].status != DIRECTORY_NOT_FOUND) 85 continue; 86 87 char *id = ids->idmap_utf8str_list_val[i]; 88 89 /* 90 * End-to-end error injection point. 91 * NEEDSWORK: should probably eliminate this for production 92 */ 93 if (uu_streq(id, " DEBUG BUILTIN ERROR ")) { 94 directory_entry_set_error(&del[i], 95 directory_error("Directory_provider_builtin.debug", 96 "Directory_provider_builtin: artificial error", 97 NULL)); 98 continue; 99 } 100 101 if (type == DIRECTORY_ID_SID[0]) 102 wksid = find_wk_by_sid(id); 103 else { 104 int idmap_id_type; 105 if (type == DIRECTORY_ID_NAME[0]) 106 idmap_id_type = IDMAP_POSIXID; 107 else if (type == DIRECTORY_ID_USER[0]) 108 idmap_id_type = IDMAP_UID; 109 else if (type == DIRECTORY_ID_GROUP[0]) 110 idmap_id_type = IDMAP_GID; 111 else { 112 directory_entry_set_error(&del[i], 113 directory_error("invalid_arg.id_type", 114 "Invalid ID type \"%1\"", 115 types, NULL)); 116 continue; 117 } 118 119 int id_len = strlen(id); 120 char name[id_len + 1]; 121 char domain[id_len + 1]; 122 123 split_name(name, domain, id); 124 125 wksid = find_wksid_by_name(name, domain, idmap_id_type); 126 } 127 128 if (wksid == NULL) 129 continue; 130 131 de = directory_provider_builtin_populate(&del[i], wksid, attrs); 132 if (de != NULL) { 133 directory_entry_set_error(&del[i], de); 134 de = NULL; 135 } 136 } 137 138 return (NULL); 139 } 140 141 /* 142 * Given a well-known name entry and a list of attributes that were 143 * requested, populate the structure to return to the caller. 144 */ 145 static 146 directory_error_t 147 directory_provider_builtin_populate( 148 directory_entry_rpc *pent, 149 const wksids_table_t *wksid, 150 idmap_utf8str_list *attrs) 151 { 152 int j; 153 directory_values_rpc *llvals; 154 int nattrs; 155 156 nattrs = attrs->idmap_utf8str_list_len; 157 158 llvals = calloc(nattrs, sizeof (directory_values_rpc)); 159 if (llvals == NULL) 160 goto nomem; 161 162 pent->status = DIRECTORY_FOUND; 163 pent->directory_entry_rpc_u.attrs.attrs_val = llvals; 164 pent->directory_entry_rpc_u.attrs.attrs_len = nattrs; 165 166 for (j = 0; j < nattrs; j++) { 167 directory_values_rpc *val; 168 char *a; 169 directory_error_t de; 170 171 /* 172 * We're going to refer to these a lot, so make a shorthand 173 * copy. 174 */ 175 a = attrs->idmap_utf8str_list_val[j]; 176 val = &llvals[j]; 177 178 /* 179 * Start by assuming no errors and that we don't have 180 * the information. 181 */ 182 val->found = FALSE; 183 de = NULL; 184 185 if (uu_strcaseeq(a, "uid")) { 186 de = str_list_dav(val, &wksid->winname, 1); 187 } else if (uu_strcaseeq(a, "uidNumber")) { 188 if (wksid->pid != IDMAP_SENTINEL_PID && 189 wksid->is_user) { 190 de = uint_list_dav(val, &wksid->pid, 1); 191 } 192 } else if (uu_strcaseeq(a, "gidNumber")) { 193 if (wksid->pid != IDMAP_SENTINEL_PID && 194 !wksid->is_user) { 195 de = uint_list_dav(val, &wksid->pid, 1); 196 } 197 } else if (uu_strcaseeq(a, "displayName") || 198 uu_strcaseeq(a, "cn")) { 199 de = str_list_dav(val, &wksid->winname, 1); 200 } else if (uu_strcaseeq(a, "distinguishedName")) { 201 char *container; 202 if (wksid->domain == NULL) { 203 container = "Users"; 204 } else { 205 container = "Builtin"; 206 } 207 RDLOCK_CONFIG(); 208 char *dn; 209 (void) asprintf(&dn, 210 "CN=%s,CN=%s,DC=%s", 211 wksid->winname, container, _idmapdstate.hostname); 212 UNLOCK_CONFIG(); 213 const char *cdn = dn; 214 de = str_list_dav(val, &cdn, 1); 215 free(dn); 216 } else if (uu_strcaseeq(a, "objectClass")) { 217 if (wksid->is_wuser) { 218 static const char *objectClasses[] = { 219 "top", 220 "person", 221 "organizationalPerson", 222 "user", 223 }; 224 de = str_list_dav(val, objectClasses, 225 UU_NELEM(objectClasses)); 226 } else { 227 static const char *objectClasses[] = { 228 "top", 229 "group", 230 }; 231 de = str_list_dav(val, objectClasses, 232 UU_NELEM(objectClasses)); 233 } 234 } else if (uu_strcaseeq(a, "objectSid")) { 235 de = sid_dav(val, wksid); 236 } else if (uu_strcaseeq(a, "x-sun-canonicalName")) { 237 char *canon; 238 239 if (wksid->domain == NULL) { 240 RDLOCK_CONFIG(); 241 (void) asprintf(&canon, "%s@%s", 242 wksid->winname, _idmapdstate.hostname); 243 UNLOCK_CONFIG(); 244 } else if (uu_streq(wksid->domain, "")) { 245 canon = strdup(wksid->winname); 246 } else { 247 (void) asprintf(&canon, "%s@%s", 248 wksid->winname, wksid->domain); 249 } 250 251 if (canon == NULL) 252 goto nomem; 253 const char *ccanon = canon; 254 de = str_list_dav(val, &ccanon, 1); 255 free(canon); 256 } else if (uu_strcaseeq(a, "x-sun-provider")) { 257 const char *provider = "Builtin"; 258 de = str_list_dav(val, &provider, 1); 259 } 260 if (de != NULL) 261 return (de); 262 } 263 264 return (NULL); 265 266 nomem: 267 return (directory_error("ENOMEM.users", 268 "No memory allocating return value for user lookup", NULL)); 269 } 270 271 /* 272 * Given a well-known name structure, generate a binary-format SID. 273 * It's a bit perverse that we must take a text-format SID and turn it into 274 * a binary-format SID, only to have the caller probably turn it back into 275 * text format, but SIDs are carried across LDAP in binary format. 276 */ 277 static 278 directory_error_t 279 sid_dav(directory_values_rpc *lvals, const wksids_table_t *wksid) 280 { 281 char *text_sid; 282 sid_t *sid; 283 directory_error_t de; 284 285 if (wksid->sidprefix == NULL) { 286 RDLOCK_CONFIG(); 287 (void) asprintf(&text_sid, "%s-%d", 288 _idmapdstate.cfg->pgcfg.machine_sid, 289 wksid->rid); 290 UNLOCK_CONFIG(); 291 } else { 292 (void) asprintf(&text_sid, "%s-%d", 293 wksid->sidprefix, wksid->rid); 294 } 295 296 if (text_sid == NULL) 297 goto nomem; 298 299 sid = sid_fromstr(text_sid); 300 free(text_sid); 301 302 if (sid == NULL) 303 goto nomem; 304 305 sid_to_le(sid); 306 307 de = bin_list_dav(lvals, sid, 1, sid_len(sid)); 308 309 sid_free(sid); 310 311 return (de); 312 313 nomem: 314 return (directory_error("ENOMEM.sid_dav", 315 "No memory allocating SID for user lookup", NULL)); 316 } 317 318 struct directory_provider_static directory_provider_builtin = { 319 "builtin", 320 directory_provider_builtin_get, 321 }; 322