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 standard UNIX users/groups. 28 * (NB: not just from files, but all nsswitch sources.) 29 */ 30 31 #include <pwd.h> 32 #include <grp.h> 33 #include <malloc.h> 34 #include <string.h> 35 #include <stdlib.h> 36 #include <netdb.h> 37 #include <libuutil.h> 38 #include <note.h> 39 #include <errno.h> 40 #include "idmapd.h" 41 #include "directory.h" 42 #include "directory_private.h" 43 #include <rpcsvc/idmap_prot.h> 44 #include "directory_server_impl.h" 45 #include "sidutil.h" 46 47 static directory_error_t machine_sid_dav(directory_values_rpc *lvals, 48 unsigned int rid); 49 static directory_error_t directory_provider_nsswitch_populate( 50 directory_entry_rpc *pent, struct passwd *pwd, struct group *grp, 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_nsswitch_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 RDLOCK_CONFIG(); 68 69 /* 6835280 spurious lint error if the strlen is in the declaration */ 70 int host_name_len = strlen(_idmapdstate.hostname); 71 char my_host_name[host_name_len + 1]; 72 (void) strcpy(my_host_name, _idmapdstate.hostname); 73 74 /* We use len later, so this is not merely a workaround for 6835280 */ 75 int machine_sid_len = strlen(_idmapdstate.cfg->pgcfg.machine_sid); 76 char my_machine_sid[machine_sid_len + 1]; 77 (void) strcpy(my_machine_sid, _idmapdstate.cfg->pgcfg.machine_sid); 78 79 UNLOCK_CONFIG(); 80 81 for (i = 0; i < ids->idmap_utf8str_list_len; i++) { 82 struct passwd *pwd = NULL; 83 struct group *grp = NULL; 84 directory_error_t de; 85 int type; 86 87 /* 88 * Extract the type for this particular ID. 89 * Advance to the next type, if it's there, else keep 90 * using this type until we run out of IDs. 91 */ 92 type = *types; 93 if (*(types+1) != '\0') 94 types++; 95 96 /* 97 * If this entry has already been handled, one way or another, 98 * skip it. 99 */ 100 if (del[i].status != DIRECTORY_NOT_FOUND) 101 continue; 102 103 char *id = ids->idmap_utf8str_list_val[i]; 104 105 if (type == DIRECTORY_ID_SID[0]) { 106 /* 107 * Is it our SID? 108 * Check whether the first part matches, then a "-", 109 * then a single RID. 110 */ 111 if (strncasecmp(id, my_machine_sid, machine_sid_len) != 112 0) 113 continue; 114 if (id[machine_sid_len] != '-') 115 continue; 116 char *p; 117 uint32_t rid = 118 strtoul(id + machine_sid_len + 1, &p, 10); 119 if (*p != '\0') 120 continue; 121 122 if (rid < LOCALRID_UID_MIN) { 123 /* Builtin, not handled here */ 124 continue; 125 } 126 127 if (rid <= LOCALRID_UID_MAX) { 128 /* User */ 129 errno = 0; 130 pwd = getpwuid(rid - LOCALRID_UID_MIN); 131 if (pwd == NULL) { 132 if (errno == 0) /* Not found */ 133 continue; 134 char buf[40]; 135 int err = errno; 136 (void) snprintf(buf, sizeof (buf), 137 "%d", err); 138 directory_entry_set_error(&del[i], 139 directory_error("errno.getpwuid", 140 "getpwuid: %2 (%1)", 141 buf, strerror(err), NULL)); 142 continue; 143 } 144 } else if (rid >= LOCALRID_GID_MIN && 145 rid <= LOCALRID_GID_MAX) { 146 /* Group */ 147 errno = 0; 148 grp = getgrgid(rid - LOCALRID_GID_MIN); 149 if (grp == NULL) { 150 if (errno == 0) /* Not found */ 151 continue; 152 char buf[40]; 153 int err = errno; 154 (void) snprintf(buf, sizeof (buf), 155 "%d", err); 156 directory_entry_set_error(&del[i], 157 directory_error("errno.getgrgid", 158 "getgrgid: %2 (%1)", 159 buf, strerror(err), NULL)); 160 continue; 161 } 162 } else 163 continue; 164 165 } else { 166 int id_len = strlen(id); 167 char name[id_len + 1]; 168 char domain[id_len + 1]; 169 170 split_name(name, domain, id); 171 172 if (domain[0] != '\0') { 173 if (!domain_eq(domain, my_host_name)) 174 continue; 175 } 176 177 /* 178 * If the caller has requested user or group 179 * information specifically, we only set one of 180 * pwd or grp. 181 * If the caller has requested either type, we try 182 * both in the hopes of getting one. 183 * Note that directory_provider_nsswitch_populate 184 * considers it to be an error if both are set. 185 */ 186 if (type != DIRECTORY_ID_GROUP[0]) { 187 /* prep for not found / error case */ 188 errno = 0; 189 190 pwd = getpwnam(name); 191 if (pwd == NULL && errno != 0) { 192 char buf[40]; 193 int err = errno; 194 (void) snprintf(buf, sizeof (buf), 195 "%d", err); 196 directory_entry_set_error(&del[i], 197 directory_error("errno.getpwnam", 198 "getpwnam: %2 (%1)", 199 buf, strerror(err), NULL)); 200 continue; 201 } 202 } 203 204 if (type != DIRECTORY_ID_USER[0]) { 205 /* prep for not found / error case */ 206 errno = 0; 207 208 grp = getgrnam(name); 209 if (grp == NULL && errno != 0) { 210 char buf[40]; 211 int err = errno; 212 (void) snprintf(buf, sizeof (buf), 213 "%d", err); 214 directory_entry_set_error(&del[i], 215 directory_error("errno.getgrnam", 216 "getgrnam: %2 (%1)", 217 buf, strerror(err), NULL)); 218 continue; 219 } 220 } 221 } 222 223 /* 224 * Didn't find it, don't populate the structure. 225 * Another provider might populate it. 226 */ 227 if (pwd == NULL && grp == NULL) 228 continue; 229 230 de = directory_provider_nsswitch_populate(&del[i], pwd, grp, 231 attrs); 232 if (de != NULL) { 233 directory_entry_set_error(&del[i], de); 234 de = NULL; 235 continue; 236 } 237 } 238 239 return (NULL); 240 } 241 242 /* 243 * Given a pwd structure or a grp structure, and a list of attributes that 244 * were requested, populate the structure to return to the caller. 245 */ 246 static 247 directory_error_t 248 directory_provider_nsswitch_populate( 249 directory_entry_rpc *pent, 250 struct passwd *pwd, 251 struct group *grp, 252 idmap_utf8str_list *attrs) 253 { 254 int j; 255 directory_values_rpc *llvals; 256 int nattrs; 257 258 /* 259 * If it wasn't for this case, everything would be a lot simpler. 260 * UNIX allows users and groups with the same name. Windows doesn't. 261 */ 262 if (pwd != NULL && grp != NULL) { 263 return directory_error("Ambiguous.Name", 264 "Ambiguous name, is both a user and a group", 265 NULL); 266 } 267 268 nattrs = attrs->idmap_utf8str_list_len; 269 270 llvals = calloc(nattrs, sizeof (directory_values_rpc)); 271 if (llvals == NULL) 272 goto nomem; 273 274 pent->directory_entry_rpc_u.attrs.attrs_val = llvals; 275 pent->directory_entry_rpc_u.attrs.attrs_len = nattrs; 276 pent->status = DIRECTORY_FOUND; 277 278 for (j = 0; j < nattrs; j++) { 279 directory_values_rpc *val; 280 char *a; 281 directory_error_t de; 282 283 /* 284 * We're going to refer to these a lot, so make a shorthand 285 * copy. 286 */ 287 a = attrs->idmap_utf8str_list_val[j]; 288 val = &llvals[j]; 289 290 /* 291 * Start by assuming no errors and that we don't have 292 * the information 293 */ 294 val->found = FALSE; 295 de = NULL; 296 297 if (pwd != NULL) { 298 /* 299 * Handle attributes for user entries. 300 */ 301 if (uu_strcaseeq(a, "cn")) { 302 const char *p = pwd->pw_name; 303 de = str_list_dav(val, &p, 1); 304 } else if (uu_strcaseeq(a, "objectClass")) { 305 static const char *objectClasses[] = { 306 "top", 307 "posixAccount", 308 }; 309 de = str_list_dav(val, objectClasses, 310 UU_NELEM(objectClasses)); 311 } else if (uu_strcaseeq(a, "gidNumber")) { 312 de = uint_list_dav(val, &pwd->pw_gid, 1); 313 } else if (uu_strcaseeq(a, "objectSid")) { 314 de = machine_sid_dav(val, 315 pwd->pw_uid + LOCALRID_UID_MIN); 316 } else if (uu_strcaseeq(a, "displayName")) { 317 const char *p = pwd->pw_gecos; 318 de = str_list_dav(val, &p, 1); 319 } else if (uu_strcaseeq(a, "distinguishedName")) { 320 char *dn; 321 RDLOCK_CONFIG(); 322 (void) asprintf(&dn, 323 "uid=%s,ou=people,dc=%s", 324 pwd->pw_name, _idmapdstate.hostname); 325 UNLOCK_CONFIG(); 326 if (dn == NULL) 327 goto nomem; 328 const char *cdn = dn; 329 de = str_list_dav(val, &cdn, 1); 330 free(dn); 331 } else if (uu_strcaseeq(a, "uid")) { 332 const char *p = pwd->pw_name; 333 de = str_list_dav(val, &p, 1); 334 } else if (uu_strcaseeq(a, "uidNumber")) { 335 de = uint_list_dav(val, &pwd->pw_uid, 1); 336 } else if (uu_strcaseeq(a, "gecos")) { 337 const char *p = pwd->pw_gecos; 338 de = str_list_dav(val, &p, 1); 339 } else if (uu_strcaseeq(a, "homeDirectory")) { 340 const char *p = pwd->pw_dir; 341 de = str_list_dav(val, &p, 1); 342 } else if (uu_strcaseeq(a, "loginShell")) { 343 const char *p = pwd->pw_shell; 344 de = str_list_dav(val, &p, 1); 345 } else if (uu_strcaseeq(a, "x-sun-canonicalName")) { 346 char *canon; 347 RDLOCK_CONFIG(); 348 (void) asprintf(&canon, "%s@%s", 349 pwd->pw_name, _idmapdstate.hostname); 350 UNLOCK_CONFIG(); 351 if (canon == NULL) 352 goto nomem; 353 const char *ccanon = canon; 354 de = str_list_dav(val, &ccanon, 1); 355 free(canon); 356 } else if (uu_strcaseeq(a, "x-sun-provider")) { 357 const char *provider = "UNIX-passwd"; 358 de = str_list_dav(val, &provider, 1); 359 } 360 } else if (grp != NULL) { 361 /* 362 * Handle attributes for group entries. 363 */ 364 if (uu_strcaseeq(a, "cn")) { 365 const char *p = grp->gr_name; 366 de = str_list_dav(val, &p, 1); 367 } else if (uu_strcaseeq(a, "objectClass")) { 368 static const char *objectClasses[] = { 369 "top", 370 "posixGroup", 371 }; 372 de = str_list_dav(val, objectClasses, 373 UU_NELEM(objectClasses)); 374 } else if (uu_strcaseeq(a, "gidNumber")) { 375 de = uint_list_dav(val, &grp->gr_gid, 1); 376 } else if (uu_strcaseeq(a, "objectSid")) { 377 de = machine_sid_dav(val, 378 grp->gr_gid + LOCALRID_GID_MIN); 379 } else if (uu_strcaseeq(a, "displayName")) { 380 const char *p = grp->gr_name; 381 de = str_list_dav(val, &p, 1); 382 } else if (uu_strcaseeq(a, "distinguishedName")) { 383 char *dn; 384 RDLOCK_CONFIG(); 385 (void) asprintf(&dn, 386 "cn=%s,ou=group,dc=%s", 387 grp->gr_name, _idmapdstate.hostname); 388 UNLOCK_CONFIG(); 389 if (dn == NULL) 390 goto nomem; 391 const char *cdn = dn; 392 de = str_list_dav(val, &cdn, 1); 393 free(dn); 394 } else if (uu_strcaseeq(a, "memberUid")) { 395 /* 396 * NEEDSWORK: There is probably a non-cast 397 * way to do this, but I don't immediately 398 * see it. 399 */ 400 const char * const *members = 401 (const char * const *)grp->gr_mem; 402 de = str_list_dav(val, members, 0); 403 } else if (uu_strcaseeq(a, "x-sun-canonicalName")) { 404 char *canon; 405 RDLOCK_CONFIG(); 406 (void) asprintf(&canon, "%s@%s", 407 grp->gr_name, _idmapdstate.hostname); 408 UNLOCK_CONFIG(); 409 if (canon == NULL) 410 goto nomem; 411 const char *ccanon = canon; 412 de = str_list_dav(val, &ccanon, 1); 413 free(canon); 414 } else if (uu_strcaseeq(a, "x-sun-provider")) { 415 const char *provider = "UNIX-group"; 416 de = str_list_dav(val, &provider, 1); 417 } 418 } 419 420 if (de != NULL) 421 return (de); 422 } 423 424 return (NULL); 425 426 nomem: 427 return (directory_error("ENOMEM.users", 428 "No memory allocating return value for user lookup", NULL)); 429 } 430 431 /* 432 * Populate a directory attribute value with a SID based on our machine SID 433 * and the specified RID. 434 * 435 * It's a bit perverse that we must take a text-format SID and turn it into 436 * a binary-format SID, only to have the caller probably turn it back into 437 * text format, but SIDs are carried across LDAP in binary format. 438 */ 439 static 440 directory_error_t 441 machine_sid_dav(directory_values_rpc *lvals, unsigned int rid) 442 { 443 sid_t *sid; 444 directory_error_t de; 445 446 RDLOCK_CONFIG(); 447 int len = strlen(_idmapdstate.cfg->pgcfg.machine_sid); 448 char buf[len + 100]; /* 100 is enough space for any RID */ 449 (void) snprintf(buf, sizeof (buf), "%s-%u", 450 _idmapdstate.cfg->pgcfg.machine_sid, rid); 451 UNLOCK_CONFIG(); 452 453 sid = sid_fromstr(buf); 454 if (sid == NULL) 455 goto nomem; 456 457 sid_to_le(sid); 458 459 de = bin_list_dav(lvals, sid, 1, sid_len(sid)); 460 sid_free(sid); 461 return (de); 462 463 nomem: 464 return (directory_error("ENOMEM.machine_sid_dav", 465 "Out of memory allocating return value for lookup", NULL)); 466 } 467 468 struct directory_provider_static directory_provider_nsswitch = { 469 "files", 470 directory_provider_nsswitch_get, 471 }; 472