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 * 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 != SENTINEL_PID && wksid->is_user) { 190 de = uint_list_dav(val, &wksid->pid, 1); 191 } 192 } else if (strcaseeq(a, "gidNumber")) { 193 if (wksid->pid != SENTINEL_PID && !wksid->is_user) { 194 de = uint_list_dav(val, &wksid->pid, 1); 195 } 196 } else if (strcaseeq(a, "displayName") || strcaseeq(a, "cn")) { 197 de = str_list_dav(val, &wksid->winname, 1); 198 } else if (strcaseeq(a, "distinguishedName")) { 199 char *container; 200 if (wksid->domain == NULL) { 201 container = "Users"; 202 } else { 203 container = "Builtin"; 204 } 205 RDLOCK_CONFIG(); 206 char *dn; 207 (void) asprintf(&dn, 208 "CN=%s,CN=%s,DC=%s", 209 wksid->winname, container, _idmapdstate.hostname); 210 UNLOCK_CONFIG(); 211 const char *cdn = dn; 212 de = str_list_dav(val, &cdn, 1); 213 free(dn); 214 } else if (strcaseeq(a, "objectClass")) { 215 if (wksid->is_wuser) { 216 static const char *objectClasses[] = { 217 "top", 218 "person", 219 "organizationalPerson", 220 "user", 221 }; 222 de = str_list_dav(val, objectClasses, 223 NELEM(objectClasses)); 224 } else { 225 static const char *objectClasses[] = { 226 "top", 227 "group", 228 }; 229 de = str_list_dav(val, objectClasses, 230 NELEM(objectClasses)); 231 } 232 } else if (strcaseeq(a, "objectSid")) { 233 de = sid_dav(val, wksid); 234 } else if (strcaseeq(a, "x-sun-canonicalName")) { 235 char *canon; 236 237 if (wksid->domain == NULL) { 238 RDLOCK_CONFIG(); 239 (void) asprintf(&canon, "%s@%s", 240 wksid->winname, _idmapdstate.hostname); 241 UNLOCK_CONFIG(); 242 } else if (streq(wksid->domain, "")) { 243 canon = strdup(wksid->winname); 244 } else { 245 (void) asprintf(&canon, "%s@%s", 246 wksid->winname, wksid->domain); 247 } 248 249 if (canon == NULL) 250 goto nomem; 251 const char *ccanon = canon; 252 de = str_list_dav(val, &ccanon, 1); 253 free(canon); 254 } else if (strcaseeq(a, "x-sun-provider")) { 255 const char *provider = "Builtin"; 256 de = str_list_dav(val, &provider, 1); 257 } 258 if (de != NULL) 259 return (de); 260 } 261 262 return (NULL); 263 264 nomem: 265 return (directory_error("ENOMEM.users", 266 "No memory allocating return value for user lookup", NULL)); 267 } 268 269 /* 270 * Given a well-known name structure, generate a binary-format SID. 271 * It's a bit perverse that we must take a text-format SID and turn it into 272 * a binary-format SID, only to have the caller probably turn it back into 273 * text format, but SIDs are carried across LDAP in binary format. 274 */ 275 static 276 directory_error_t 277 sid_dav(directory_values_rpc *lvals, const wksids_table_t *wksid) 278 { 279 char *text_sid; 280 sid_t *sid; 281 directory_error_t de; 282 283 if (wksid->sidprefix == NULL) { 284 RDLOCK_CONFIG(); 285 (void) asprintf(&text_sid, "%s-%d", 286 _idmapdstate.cfg->pgcfg.machine_sid, 287 wksid->rid); 288 UNLOCK_CONFIG(); 289 } else { 290 (void) asprintf(&text_sid, "%s-%d", 291 wksid->sidprefix, wksid->rid); 292 } 293 294 if (text_sid == NULL) 295 goto nomem; 296 297 sid = sid_fromstr(text_sid); 298 free(text_sid); 299 300 if (sid == NULL) 301 goto nomem; 302 303 sid_to_le(sid); 304 305 de = bin_list_dav(lvals, sid, 1, sid_len(sid)); 306 307 sid_free(sid); 308 309 return (de); 310 311 nomem: 312 return (directory_error("ENOMEM.sid_dav", 313 "No memory allocating SID for user lookup", NULL)); 314 } 315 316 struct directory_provider_static directory_provider_builtin = { 317 "builtin", 318 directory_provider_builtin_get, 319 }; 320