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 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Retrieve directory information for built-in users and groups 29 */ 30 31 #include <stdio.h> 32 #include <limits.h> 33 #include <sys/idmap.h> 34 #include <sys/param.h> 35 #include <string.h> 36 #include <stdlib.h> 37 #include <netdb.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 "miscutils.h" 45 #include "sidutil.h" 46 47 static directory_error_t sid_dav(directory_values_rpc *lvals, 48 const wksids_table_t *wksid); 49 static directory_error_t directory_provider_builtin_populate( 50 directory_entry_rpc *pent, const wksids_table_t *wksid, 51 idmap_utf8str_list *attrs); 52 53 /* 54 * Retrieve information by name. 55 * Called indirectly through the directory_provider_static structure. 56 */ 57 static 58 directory_error_t 59 directory_provider_builtin_get( 60 directory_entry_rpc *del, 61 idmap_utf8str_list *ids, 62 idmap_utf8str types, 63 idmap_utf8str_list *attrs) 64 { 65 int i; 66 67 for (i = 0; i < ids->idmap_utf8str_list_len; i++) { 68 const wksids_table_t *wksid; 69 directory_error_t de; 70 int type; 71 72 /* 73 * Extract the type for this particular ID. 74 * Advance to the next type, if it's there, else keep 75 * using this type until we run out of IDs. 76 */ 77 type = *types; 78 if (*(types+1) != '\0') 79 types++; 80 81 /* 82 * If this entry has already been handled, one way or another, 83 * skip it. 84 */ 85 if (del[i].status != DIRECTORY_NOT_FOUND) 86 continue; 87 88 char *id = ids->idmap_utf8str_list_val[i]; 89 90 /* 91 * End-to-end error injection point. 92 * NEEDSWORK: should probably eliminate this for production 93 */ 94 if (streq(id, " DEBUG BUILTIN ERROR ")) { 95 directory_entry_set_error(&del[i], 96 directory_error("Directory_provider_builtin.debug", 97 "Directory_provider_builtin: artificial error", 98 NULL)); 99 continue; 100 } 101 102 if (type == DIRECTORY_ID_SID[0]) 103 wksid = find_wk_by_sid(id); 104 else { 105 int idmap_id_type; 106 if (type == DIRECTORY_ID_NAME[0]) 107 idmap_id_type = IDMAP_POSIXID; 108 else if (type == DIRECTORY_ID_USER[0]) 109 idmap_id_type = IDMAP_UID; 110 else if (type == DIRECTORY_ID_GROUP[0]) 111 idmap_id_type = IDMAP_GID; 112 else { 113 directory_entry_set_error(&del[i], 114 directory_error("invalid_arg.id_type", 115 "Invalid ID type \"%1\"", 116 types, NULL)); 117 continue; 118 } 119 120 int id_len = strlen(id); 121 char name[id_len + 1]; 122 char domain[id_len + 1]; 123 124 split_name(name, domain, id); 125 126 wksid = find_wksid_by_name(name, domain, idmap_id_type); 127 } 128 129 if (wksid == NULL) 130 continue; 131 132 de = directory_provider_builtin_populate(&del[i], wksid, attrs); 133 if (de != NULL) { 134 directory_entry_set_error(&del[i], de); 135 de = NULL; 136 } 137 } 138 139 return (NULL); 140 } 141 142 /* 143 * Given a well-known name entry and a list of attributes that were 144 * requested, populate the structure to return to the caller. 145 */ 146 static 147 directory_error_t 148 directory_provider_builtin_populate( 149 directory_entry_rpc *pent, 150 const wksids_table_t *wksid, 151 idmap_utf8str_list *attrs) 152 { 153 int j; 154 directory_values_rpc *llvals; 155 int nattrs; 156 157 nattrs = attrs->idmap_utf8str_list_len; 158 159 llvals = calloc(nattrs, sizeof (directory_values_rpc)); 160 if (llvals == NULL) 161 goto nomem; 162 163 pent->status = DIRECTORY_FOUND; 164 pent->directory_entry_rpc_u.attrs.attrs_val = llvals; 165 pent->directory_entry_rpc_u.attrs.attrs_len = nattrs; 166 167 for (j = 0; j < nattrs; j++) { 168 directory_values_rpc *val; 169 char *a; 170 directory_error_t de; 171 172 /* 173 * We're going to refer to these a lot, so make a shorthand 174 * copy. 175 */ 176 a = attrs->idmap_utf8str_list_val[j]; 177 val = &llvals[j]; 178 179 /* 180 * Start by assuming no errors and that we don't have 181 * the information. 182 */ 183 val->found = FALSE; 184 de = NULL; 185 186 if (strcaseeq(a, "uid")) { 187 de = str_list_dav(val, &wksid->winname, 1); 188 } else if (strcaseeq(a, "uidNumber")) { 189 if (wksid->pid != IDMAP_SENTINEL_PID && 190 wksid->is_user) { 191 de = uint_list_dav(val, &wksid->pid, 1); 192 } 193 } else if (strcaseeq(a, "gidNumber")) { 194 if (wksid->pid != IDMAP_SENTINEL_PID && 195 !wksid->is_user) { 196 de = uint_list_dav(val, &wksid->pid, 1); 197 } 198 } else if (strcaseeq(a, "displayName") || strcaseeq(a, "cn")) { 199 de = str_list_dav(val, &wksid->winname, 1); 200 } else if (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 (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 NELEM(objectClasses)); 226 } else { 227 static const char *objectClasses[] = { 228 "top", 229 "group", 230 }; 231 de = str_list_dav(val, objectClasses, 232 NELEM(objectClasses)); 233 } 234 } else if (strcaseeq(a, "objectSid")) { 235 de = sid_dav(val, wksid); 236 } else if (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 (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 (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