Home | History | Annotate | Download | only in idmapd
      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