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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <grp.h> 30 #include "ldap_common.h" 31 32 /* String which may need to be removed from beginning of group password */ 33 #define _CRYPT "{CRYPT}" 34 #define _NO_PASSWD_VAL "" 35 36 /* Group attributes filters */ 37 #define _G_NAME "cn" 38 #define _G_GID "gidnumber" 39 #define _G_PASSWD "userpassword" 40 #define _G_MEM "memberuid" 41 42 #define _F_GETGRNAM "(&(objectClass=posixGroup)(cn=%s))" 43 #define _F_GETGRNAM_SSD "(&(%%s)(cn=%s))" 44 #define _F_GETGRGID "(&(objectClass=posixGroup)(gidNumber=%ld))" 45 #define _F_GETGRGID_SSD "(&(%%s)(gidNumber=%ld))" 46 #define _F_GETGRMEM "(&(objectClass=posixGroup)(memberUid=%s))" 47 #define _F_GETGRMEM_SSD "(&(%%s)(memberUid=%s))" 48 49 static const char *gr_attrs[] = { 50 _G_NAME, 51 _G_GID, 52 _G_PASSWD, 53 _G_MEM, 54 (char *)NULL 55 }; 56 57 58 /* 59 * _nss_ldap_group2ent is the data marshaling method for the group getXbyY 60 * (e.g., getgrnam(), getgrgid(), getgrent()) backend processes. This method 61 * is called after a successful ldap search has been performed. This method 62 * will parse the ldap search values into struct group = argp->buf.buffer 63 * which the frontend process expects. Three error conditions are expected 64 * and returned to nsswitch. 65 */ 66 67 static int 68 _nss_ldap_group2ent(ldap_backend_ptr be, nss_XbyY_args_t *argp) 69 { 70 int i, j; 71 int nss_result; 72 int buflen = (int)0; 73 int firstime = (int)1; 74 unsigned long len = 0L; 75 char **mp = NULL; 76 char *val = (char *)NULL; 77 char *buffer = (char *)NULL; 78 char *ceiling = (char *)NULL; 79 struct group *grp = (struct group *)NULL; 80 ns_ldap_result_t *result = be->result; 81 ns_ldap_attr_t *attrptr; 82 83 buffer = argp->buf.buffer; 84 buflen = (size_t)argp->buf.buflen; 85 if (!argp->buf.result) { 86 nss_result = (int)NSS_STR_PARSE_ERANGE; 87 goto result_grp2ent; 88 } 89 grp = (struct group *)argp->buf.result; 90 ceiling = buffer + buflen; 91 mp = grp->gr_mem = (char **)NULL; 92 93 /* initialize no group password */ 94 grp->gr_passwd = (char *)NULL; 95 nss_result = (int)NSS_STR_PARSE_SUCCESS; 96 (void) memset(argp->buf.buffer, 0, buflen); 97 98 attrptr = getattr(result, 0); 99 if (attrptr == NULL) { 100 nss_result = (int)NSS_STR_PARSE_PARSE; 101 goto result_grp2ent; 102 } 103 104 for (i = 0; i < result->entry->attr_count; i++) { 105 attrptr = getattr(result, i); 106 if (attrptr == NULL) { 107 nss_result = (int)NSS_STR_PARSE_PARSE; 108 goto result_grp2ent; 109 } 110 if (strcasecmp(attrptr->attrname, _G_NAME) == 0) { 111 if ((attrptr->attrvalue[0] == NULL) || 112 (len = strlen(attrptr->attrvalue[0])) < 1) { 113 nss_result = (int)NSS_STR_PARSE_PARSE; 114 goto result_grp2ent; 115 } 116 grp->gr_name = buffer; 117 buffer += len + 1; 118 if (buffer > ceiling) { 119 nss_result = (int)NSS_STR_PARSE_ERANGE; 120 goto result_grp2ent; 121 } 122 (void) strcpy(grp->gr_name, attrptr->attrvalue[0]); 123 continue; 124 } 125 if (strcasecmp(attrptr->attrname, _G_PASSWD) == 0) { 126 val = attrptr->attrvalue[0]; 127 /* 128 * Preen "{crypt}" if necessary. 129 * If the password does not include the {crypt} prefix 130 * then the password may be plain text. And thus 131 * perhaps crypt(3c) should be used to encrypt it. 132 * Currently the password is copied verbatim. 133 */ 134 if (strncasecmp(val, _CRYPT, 135 (sizeof (_CRYPT) - 1)) == 0) 136 val += (sizeof (_CRYPT) - 1); 137 len = strlen(val); 138 grp->gr_passwd = buffer; 139 buffer += len + 1; 140 if (buffer > ceiling) { 141 nss_result = (int)NSS_STR_PARSE_ERANGE; 142 goto result_grp2ent; 143 } 144 (void) strcpy(grp->gr_passwd, val); 145 continue; 146 } 147 if (strcasecmp(attrptr->attrname, _G_GID) == 0) { 148 if (strlen(attrptr->attrvalue[0]) == 0) { 149 nss_result = (int)NSS_STR_PARSE_PARSE; 150 goto result_grp2ent; 151 } 152 errno = 0; 153 grp->gr_gid = (gid_t)strtol(attrptr->attrvalue[0], 154 (char **)NULL, 10); 155 if (errno != 0) { 156 nss_result = (int)NSS_STR_PARSE_PARSE; 157 goto result_grp2ent; 158 } 159 continue; 160 } 161 if (strcasecmp(attrptr->attrname, _G_MEM) == 0) { 162 for (j = 0; j < attrptr->value_count; j++) { 163 if (firstime) { 164 mp = grp->gr_mem = 165 (char **)ROUND_UP(buffer, 166 sizeof (char **)); 167 buffer = (char *)grp->gr_mem + 168 sizeof (char *) * 169 (attrptr->value_count + 1); 170 buffer = (char *)ROUND_UP(buffer, 171 sizeof (char **)); 172 if (buffer > ceiling) { 173 nss_result = 174 (int)NSS_STR_PARSE_ERANGE; 175 goto result_grp2ent; 176 } 177 firstime = (int)0; 178 } 179 if (attrptr->attrvalue[j] == NULL) { 180 nss_result = (int)NSS_STR_PARSE_PARSE; 181 goto result_grp2ent; 182 } 183 len = strlen(attrptr->attrvalue[j]); 184 if (len == 0) 185 continue; 186 *mp = buffer; 187 buffer += len + 1; 188 if (buffer > ceiling) { 189 nss_result = (int)NSS_STR_PARSE_ERANGE; 190 goto result_grp2ent; 191 } 192 (void) strcpy(*mp++, attrptr->attrvalue[j]); 193 continue; 194 } 195 } 196 } 197 /* Don't leave password as null */ 198 if (grp->gr_passwd == (char *)NULL) { 199 /* 200 * The password may be missing; rfc2307bis defines 201 * the 'posixGroup' attributes 'authPassword' and 202 * 'userPassword' as being optional. Or a directory 203 * access control may be preventing us from reading 204 * the password. Currently we don't know which it is. 205 * If it's an access problem then perhaps the password 206 * should be set to "*NP*". But for now a simple empty 207 * string is returned. 208 */ 209 grp->gr_passwd = buffer; 210 buffer += sizeof (_NO_PASSWD_VAL); 211 if (buffer > ceiling) { 212 nss_result = (int)NSS_STR_PARSE_ERANGE; 213 goto result_grp2ent; 214 } 215 (void) strcpy(grp->gr_passwd, _NO_PASSWD_VAL); 216 } 217 if (mp == NULL) { 218 mp = grp->gr_mem = (char **)ROUND_UP(buffer, sizeof (char **)); 219 buffer = (char *)grp->gr_mem + sizeof (char *); 220 buffer = (char *)ROUND_UP(buffer, sizeof (char **)); 221 if (buffer > ceiling) { 222 nss_result = (int)NSS_STR_PARSE_ERANGE; 223 goto result_grp2ent; 224 } 225 } 226 *mp = NULL; 227 228 #ifdef DEBUG 229 (void) fprintf(stdout, "\n[getgrent.c: _nss_ldap_group2ent]\n"); 230 (void) fprintf(stdout, " gr_name: [%s]\n", grp->gr_name); 231 if (grp->gr_passwd != (char *)NULL) 232 (void) fprintf(stdout, " gr_passwd: [%s]\n", 233 grp->gr_passwd); 234 (void) fprintf(stdout, " gr_gid: [%ld]\n", grp->gr_gid); 235 if (mp != NULL) { 236 for (mp = grp->gr_mem; *mp != NULL; mp++) 237 (void) fprintf(stdout, " gr_mem: [%s]\n", *mp); 238 } 239 #endif /* DEBUG */ 240 241 result_grp2ent: 242 243 (void) __ns_ldap_freeResult(&be->result); 244 return ((int)nss_result); 245 } 246 247 248 /* 249 * getbynam gets a group entry by name. This function constructs an ldap 250 * search filter using the name invocation parameter and the getgrnam search 251 * filter defined. Once the filter is constructed, we searche for a matching 252 * entry and marshal the data results into struct group for the frontend 253 * process. The function _nss_ldap_group2ent performs the data marshaling. 254 */ 255 256 static nss_status_t 257 getbynam(ldap_backend_ptr be, void *a) 258 { 259 nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; 260 char searchfilter[SEARCHFILTERLEN]; 261 char userdata[SEARCHFILTERLEN]; 262 char groupname[SEARCHFILTERLEN]; 263 int ret; 264 265 #ifdef DEBUG 266 (void) fprintf(stdout, "\n[getgrent.c: getbyname]\n"); 267 #endif /* DBEUG */ 268 if (_ldap_filter_name(groupname, argp->key.name, sizeof (groupname)) 269 != 0) 270 return ((nss_status_t)NSS_NOTFOUND); 271 272 ret = snprintf(searchfilter, sizeof (searchfilter), 273 _F_GETGRNAM, groupname); 274 if (ret >= sizeof (searchfilter) || ret < 0) 275 return ((nss_status_t)NSS_NOTFOUND); 276 277 ret = snprintf(userdata, sizeof (userdata), _F_GETGRNAM_SSD, groupname); 278 if (ret >= sizeof (userdata) || ret < 0) 279 return ((nss_status_t)NSS_NOTFOUND); 280 281 return ((nss_status_t)_nss_ldap_lookup(be, argp, 282 _GROUP, searchfilter, NULL, 283 _merge_SSD_filter, userdata)); 284 } 285 286 287 /* 288 * getbygid gets a group entry by number. This function constructs an ldap 289 * search filter using the name invocation parameter and the getgrgid search 290 * filter defined. Once the filter is constructed, we searche for a matching 291 * entry and marshal the data results into struct group for the frontend 292 * process. The function _nss_ldap_group2ent performs the data marshaling. 293 */ 294 295 static nss_status_t 296 getbygid(ldap_backend_ptr be, void *a) 297 { 298 nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; 299 char searchfilter[SEARCHFILTERLEN]; 300 char userdata[SEARCHFILTERLEN]; 301 int ret; 302 303 #ifdef DEBUG 304 (void) fprintf(stdout, "\n[getgrent.c: getbygid]\n"); 305 #endif /* DBEUG */ 306 ret = snprintf(searchfilter, sizeof (searchfilter), 307 _F_GETGRGID, (long)argp->key.uid); 308 if (ret >= sizeof (searchfilter) || ret < 0) 309 return ((nss_status_t)NSS_NOTFOUND); 310 311 ret = snprintf(userdata, sizeof (userdata), 312 _F_GETGRGID_SSD, (long)argp->key.uid); 313 if (ret >= sizeof (userdata) || ret < 0) 314 return ((nss_status_t)NSS_NOTFOUND); 315 316 return ((nss_status_t)_nss_ldap_lookup(be, argp, 317 _GROUP, searchfilter, NULL, 318 _merge_SSD_filter, userdata)); 319 320 } 321 322 323 /* 324 * getbymember returns all groups a user is defined in. This function 325 * uses different architectural procedures than the other group backend 326 * system calls because it's a private interface. This function constructs 327 * an ldap search filter using the name invocation parameter. Once the 328 * filter is constructed, we search for all matching groups counting 329 * and storing each group name, gid, etc. Data marshaling is used for 330 * group processing. The function _nss_ldap_group2ent() performs the 331 * data marshaling. 332 * 333 * (const char *)argp->username; (size_t)strlen(argp->username); 334 * (gid_t)argp->gid_array; (int)argp->maxgids; 335 * (int)argp->numgids; 336 */ 337 338 static nss_status_t 339 getbymember(ldap_backend_ptr be, void *a) 340 { 341 int i, j, k; 342 int gcnt = (int)0; 343 char **groupvalue, **membervalue; 344 nss_status_t lstat; 345 nss_XbyY_args_t argb; 346 static nss_XbyY_buf_t *gb; 347 struct nss_groupsbymem *argp = (struct nss_groupsbymem *)a; 348 char searchfilter[SEARCHFILTERLEN]; 349 char userdata[SEARCHFILTERLEN]; 350 char name[SEARCHFILTERLEN]; 351 ns_ldap_result_t *result; 352 ns_ldap_entry_t *curEntry; 353 char *username; 354 gid_t gid; 355 int ret; 356 357 #ifdef DEBUG 358 (void) fprintf(stdout, "\n[getgrent.c: getbymember]\n"); 359 #endif /* DBEUG */ 360 361 NSS_XbyY_ALLOC(&gb, sizeof (struct group), NSS_BUFLEN_GROUP); 362 NSS_XbyY_INIT(&argb, gb->result, gb->buffer, gb->buflen, 0); 363 364 if (strcmp(argp->username, "") == 0 || 365 strcmp(argp->username, "root") == 0) 366 return ((nss_status_t)NSS_NOTFOUND); 367 368 if (_ldap_filter_name(name, argp->username, sizeof (name)) != 0) 369 return ((nss_status_t)NSS_NOTFOUND); 370 371 ret = snprintf(searchfilter, sizeof (searchfilter), _F_GETGRMEM, name); 372 if (ret >= sizeof (searchfilter) || ret < 0) 373 return ((nss_status_t)NSS_NOTFOUND); 374 375 ret = snprintf(userdata, sizeof (userdata), _F_GETGRMEM_SSD, name); 376 if (ret >= sizeof (userdata) || ret < 0) 377 return ((nss_status_t)NSS_NOTFOUND); 378 379 gcnt = (int)argp->numgids; 380 lstat = (nss_status_t)_nss_ldap_nocb_lookup(be, &argb, 381 _GROUP, searchfilter, NULL, 382 _merge_SSD_filter, userdata); 383 if (lstat != (nss_status_t)NS_LDAP_SUCCESS) 384 return ((nss_status_t)lstat); 385 if (be->result == NULL) 386 return (NSS_NOTFOUND); 387 username = (char *)argp->username; 388 result = (ns_ldap_result_t *)be->result; 389 curEntry = (ns_ldap_entry_t *)result->entry; 390 for (i = 0; i < result->entries_count; i++) { 391 membervalue = __ns_ldap_getAttr(curEntry, "memberUid"); 392 if (membervalue) { 393 for (j = 0; membervalue[j]; j++) { 394 if (strcmp(membervalue[j], username) == NULL) { 395 groupvalue = __ns_ldap_getAttr(curEntry, 396 "gidnumber"); 397 gid = (gid_t)strtol(groupvalue[0], 398 (char **)NULL, 10); 399 if (argp->numgids < argp->maxgids) { 400 for (k = 0; k < argp->numgids; 401 k++) { 402 if (argp->gid_array[k] == gid) 403 /* already exists */ 404 break; 405 } 406 if (k == argp->numgids) 407 argp->gid_array[argp->numgids++] 408 = gid; 409 } 410 break; 411 } 412 } 413 } 414 curEntry = curEntry->next; 415 } 416 417 __ns_ldap_freeResult((ns_ldap_result_t **)&be->result); 418 NSS_XbyY_FREE(&gb); 419 if (gcnt == argp->numgids) 420 return ((nss_status_t)NSS_NOTFOUND); 421 422 return ((nss_status_t)NSS_SUCCESS); 423 } 424 425 static ldap_backend_op_t gr_ops[] = { 426 _nss_ldap_destr, 427 _nss_ldap_endent, 428 _nss_ldap_setent, 429 _nss_ldap_getent, 430 getbynam, 431 getbygid, 432 getbymember 433 }; 434 435 436 /*ARGSUSED0*/ 437 nss_backend_t * 438 _nss_ldap_group_constr(const char *dummy1, const char *dummy2, 439 const char *dummy3) 440 { 441 442 return ((nss_backend_t *)_nss_ldap_constr(gr_ops, 443 sizeof (gr_ops)/sizeof (gr_ops[0]), _GROUP, gr_attrs, 444 _nss_ldap_group2ent)); 445 } 446