1/* group.c - group lookup routines */ 2/* $OpenLDAP$ */ 3/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 4 * 5 * Copyright 2008-2011 The OpenLDAP Foundation. 6 * Portions Copyright 2008-2009 by Howard Chu, Symas Corp. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted only as authorized by the OpenLDAP 11 * Public License. 12 * 13 * A copy of this license is available in the file LICENSE in the 14 * top-level directory of the distribution or, alternatively, at 15 * <http://www.OpenLDAP.org/license.html>. 16 */ 17/* ACKNOWLEDGEMENTS: 18 * This code references portions of the nss-ldapd package 19 * written by Arthur de Jong. The nss-ldapd code was forked 20 * from the nss-ldap library written by Luke Howard. 21 */ 22 23#include "nssov.h" 24 25/* for gid_t */ 26#include <grp.h> 27 28/* ( nisSchema.2.2 NAME 'posixGroup' SUP top STRUCTURAL 29 * DESC 'Abstraction of a group of accounts' 30 * MUST ( cn $ gidNumber ) 31 * MAY ( userPassword $ memberUid $ description ) ) 32 * 33 * apart from that the above the uniqueMember attributes may be 34 * supported in a coming release (they map to DNs, which is an extra 35 * lookup step) 36 * 37 * using nested groups (groups that are member of a group) is currently 38 * not supported, this may be added in a later release 39 */ 40 41/* the basic search filter for searches */ 42static struct berval group_filter = BER_BVC("(objectClass=posixGroup)"); 43 44/* the attributes to request with searches */ 45static struct berval group_keys[] = { 46 BER_BVC("cn"), 47 BER_BVC("userPassword"), 48 BER_BVC("gidNumber"), 49 BER_BVC("memberUid"), 50 BER_BVC("uniqueMember"), 51 BER_BVNULL 52}; 53 54#define CN_KEY 0 55#define PWD_KEY 1 56#define GID_KEY 2 57#define UID_KEY 3 58#define MEM_KEY 4 59 60/* default values for attributes */ 61static struct berval default_group_userPassword = BER_BVC("*"); /* unmatchable */ 62 63NSSOV_CBPRIV(group, 64 nssov_info *ni; 65 char buf[256]; 66 struct berval name; 67 struct berval gidnum; 68 struct berval user; 69 int wantmembers;); 70 71/* create a search filter for searching a group entry 72 by member uid, return -1 on errors */ 73static int mkfilter_group_bymember(nssov_group_cbp *cbp,struct berval *buf) 74{ 75 struct berval dn; 76 /* try to translate uid to DN */ 77 nssov_uid2dn(cbp->op,cbp->ni,&cbp->user,&dn); 78 if (BER_BVISNULL(&dn)) { 79 if (cbp->user.bv_len + cbp->mi->mi_filter.bv_len + cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_len + 6 > 80 buf->bv_len ) 81 return -1; 82 buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))", 83 cbp->mi->mi_filter.bv_val, cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val, 84 cbp->user.bv_val ); 85 } else { /* also lookup using user DN */ 86 if (cbp->user.bv_len + cbp->mi->mi_filter.bv_len + cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_len + 87 dn.bv_len + cbp->mi->mi_attrs[MEM_KEY].an_desc->ad_cname.bv_len + 12 > buf->bv_len ) 88 return -1; 89 buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(|(%s=%s)(%s=%s)))", 90 cbp->mi->mi_filter.bv_val, 91 cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val, cbp->user.bv_val, 92 cbp->mi->mi_attrs[MEM_KEY].an_desc->ad_cname.bv_val, dn.bv_val ); 93 } 94 return 0; 95} 96 97NSSOV_INIT(group) 98 99/* 100 Checks to see if the specified name is a valid group name. 101 102 This test is based on the definition from POSIX (IEEE Std 1003.1, 2004, 103 3.189 Group Name and 3.276 Portable Filename Character Set): 104 http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_189 105 http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_276 106 107 The standard defines group names valid if they only contain characters from 108 the set [A-Za-z0-9._-] where the hyphen should not be used as first 109 character. 110*/ 111static int isvalidgroupname(struct berval *name) 112{ 113 int i; 114 115 if ( !name->bv_val || !name->bv_len ) 116 return 0; 117 /* check first character */ 118 if ( ! ( (name->bv_val[0]>='A' && name->bv_val[0] <= 'Z') || 119 (name->bv_val[0]>='a' && name->bv_val[0] <= 'z') || 120 (name->bv_val[0]>='0' && name->bv_val[0] <= '9') || 121 name->bv_val[0]=='.' || name->bv_val[0]=='_' ) ) 122 return 0; 123 /* check other characters */ 124 for (i=1;i<name->bv_len;i++) 125 { 126#ifndef STRICT_GROUPS 127 /* allow spaces too */ 128 if (name->bv_val[i] == ' ') continue; 129#endif 130 if ( ! ( (name->bv_val[i]>='A' && name->bv_val[i] <= 'Z') || 131 (name->bv_val[i]>='a' && name->bv_val[i] <= 'z') || 132 (name->bv_val[i]>='0' && name->bv_val[i] <= '9') || 133 name->bv_val[i]=='.' || name->bv_val[i]=='_' || name->bv_val[i]=='-') ) 134 return 0; 135 } 136 /* no test failed so it must be good */ 137 return -1; 138} 139 140static int write_group(nssov_group_cbp *cbp,Entry *entry) 141{ 142 struct berval tmparr[2], tmpgid[2]; 143 struct berval *names,*gids,*members; 144 struct berval passwd = {0}; 145 Attribute *a; 146 int i,j,nummembers,rc; 147 148 /* get group name (cn) */ 149 if (BER_BVISNULL(&cbp->name)) 150 { 151 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[CN_KEY].an_desc); 152 if ( !a ) 153 { 154 Debug(LDAP_DEBUG_ANY,"group entry %s does not contain %s value\n", 155 entry->e_name.bv_val, cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val,0); 156 return 0; 157 } 158 names = a->a_vals; 159 } 160 else 161 { 162 names=tmparr; 163 names[0]=cbp->name; 164 BER_BVZERO(&names[1]); 165 } 166 /* get the group id(s) */ 167 if (BER_BVISNULL(&cbp->gidnum)) 168 { 169 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GID_KEY].an_desc); 170 if ( !a ) 171 { 172 Debug(LDAP_DEBUG_ANY,"group entry %s does not contain %s value\n", 173 entry->e_name.bv_val, cbp->mi->mi_attrs[GID_KEY].an_desc->ad_cname.bv_val,0); 174 return 0; 175 } 176 gids = a->a_vals; 177 } 178 else 179 { 180 gids=tmpgid; 181 gids[0]=cbp->gidnum; 182 BER_BVZERO(&gids[1]); 183 } 184 /* get group passwd (userPassword) (use only first entry) */ 185 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[PWD_KEY].an_desc); 186 if (a) 187 get_userpassword(&a->a_vals[0], &passwd); 188 if (BER_BVISNULL(&passwd)) 189 passwd=default_group_userPassword; 190 /* get group members (memberUid&uniqueMember) */ 191 if (cbp->wantmembers) { 192 Attribute *b; 193 i = 0; j = 0; 194 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UID_KEY].an_desc); 195 b = attr_find(entry->e_attrs, cbp->mi->mi_attrs[MEM_KEY].an_desc); 196 if ( a ) 197 i += a->a_numvals; 198 if ( b ) 199 i += b->a_numvals; 200 if ( i ) { 201 members = cbp->op->o_tmpalloc( (i+1) * sizeof(struct berval), cbp->op->o_tmpmemctx ); 202 203 if ( a ) { 204 for (i=0; i<a->a_numvals; i++) { 205 if (isvalidusername(&a->a_vals[i])) { 206 ber_dupbv_x(&members[j],&a->a_vals[i],cbp->op->o_tmpmemctx); 207 j++; 208 } 209 } 210 } 211 a = b; 212 if ( a ) { 213 for (i=0; i<a->a_numvals; i++) { 214 if (nssov_dn2uid(cbp->op,cbp->ni,&a->a_nvals[i],&members[j])) 215 j++; 216 } 217 } 218 nummembers = j; 219 BER_BVZERO(&members[j]); 220 } else { 221 members=NULL; 222 nummembers = 0; 223 } 224 225 } else { 226 members=NULL; 227 nummembers = 0; 228 } 229 /* write entries for all names and gids */ 230 for (i=0;!BER_BVISNULL(&names[i]);i++) 231 { 232 if (!isvalidgroupname(&names[i])) 233 { 234 Debug(LDAP_DEBUG_ANY,"nssov: group entry %s contains invalid group name: \"%s\"\n", 235 entry->e_name.bv_val,names[i].bv_val,0); 236 } 237 else 238 { 239 for (j=0;!BER_BVISNULL(&gids[j]);j++) 240 { 241 char *tmp; 242 int tmpint32; 243 gid_t gid; 244 gid = strtol(gids[j].bv_val, &tmp, 0); 245 if ( *tmp ) { 246 Debug(LDAP_DEBUG_ANY,"nssov: group entry %s contains non-numeric %s value: \"%s\"\n", 247 entry->e_name.bv_val, cbp->mi->mi_attrs[GID_KEY].an_desc->ad_cname.bv_val, 248 names[i].bv_val); 249 continue; 250 } 251 WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN); 252 WRITE_BERVAL(cbp->fp,&names[i]); 253 WRITE_BERVAL(cbp->fp,&passwd); 254 WRITE_TYPE(cbp->fp,gid,gid_t); 255 /* write a list of values */ 256 WRITE_INT32(cbp->fp,nummembers); 257 if (nummembers) 258 { 259 int k; 260 for (k=0;k<nummembers;k++) { 261 WRITE_BERVAL(cbp->fp,&members[k]); 262 } 263 } 264 } 265 } 266 } 267 /* free and return */ 268 if (members!=NULL) 269 ber_bvarray_free_x( members, cbp->op->o_tmpmemctx ); 270 return rc; 271} 272 273NSSOV_CB(group) 274 275NSSOV_HANDLE( 276 group,byname, 277 char fbuf[1024]; 278 struct berval filter = {sizeof(fbuf)}; 279 filter.bv_val = fbuf; 280 READ_STRING(fp,cbp.buf); 281 cbp.name.bv_len = tmpint32; 282 cbp.name.bv_val = cbp.buf; 283 if (!isvalidgroupname(&cbp.name)) { 284 Debug(LDAP_DEBUG_ANY,"nssov_group_byname(%s): invalid group name\n",cbp.name.bv_val,0,0); 285 return -1; 286 } 287 cbp.wantmembers = 1; 288 cbp.ni = ni; 289 BER_BVZERO(&cbp.gidnum); 290 BER_BVZERO(&cbp.user);, 291 Debug(LDAP_DEBUG_TRACE,"nslcd_group_byname(%s)\n",cbp.name.bv_val,0,0);, 292 NSLCD_ACTION_GROUP_BYNAME, 293 nssov_filter_byname(cbp.mi,CN_KEY,&cbp.name,&filter) 294) 295 296NSSOV_HANDLE( 297 group,bygid, 298 gid_t gid; 299 char fbuf[1024]; 300 struct berval filter = {sizeof(fbuf)}; 301 filter.bv_val = fbuf; 302 READ_TYPE(fp,gid,gid_t); 303 cbp.gidnum.bv_val = cbp.buf; 304 cbp.gidnum.bv_len = snprintf(cbp.buf,sizeof(cbp.buf),"%d",gid); 305 cbp.wantmembers = 1; 306 cbp.ni = ni; 307 BER_BVZERO(&cbp.name); 308 BER_BVZERO(&cbp.user);, 309 Debug(LDAP_DEBUG_TRACE,"nssov_group_bygid(%s)\n",cbp.gidnum.bv_val,0,0);, 310 NSLCD_ACTION_GROUP_BYGID, 311 nssov_filter_byid(cbp.mi,GID_KEY,&cbp.gidnum,&filter) 312) 313 314NSSOV_HANDLE( 315 group,bymember, 316 char fbuf[1024]; 317 struct berval filter = {sizeof(fbuf)}; 318 filter.bv_val = fbuf; 319 READ_STRING(fp,cbp.buf); 320 cbp.user.bv_len = tmpint32; 321 cbp.user.bv_val = cbp.buf; 322 if (!isvalidusername(&cbp.user)) { 323 Debug(LDAP_DEBUG_ANY,"nssov_group_bymember(%s): invalid user name\n",cbp.user.bv_val,0,0); 324 return -1; 325 } 326 cbp.wantmembers = 0; 327 cbp.ni = ni; 328 BER_BVZERO(&cbp.name); 329 BER_BVZERO(&cbp.gidnum);, 330 Debug(LDAP_DEBUG_TRACE,"nssov_group_bymember(%s)\n",cbp.user.bv_val,0,0);, 331 NSLCD_ACTION_GROUP_BYMEMBER, 332 mkfilter_group_bymember(&cbp,&filter) 333) 334 335NSSOV_HANDLE( 336 group,all, 337 struct berval filter; 338 /* no parameters to read */ 339 cbp.wantmembers = 1; 340 cbp.ni = ni; 341 BER_BVZERO(&cbp.name); 342 BER_BVZERO(&cbp.gidnum);, 343 Debug(LDAP_DEBUG_TRACE,"nssov_group_all()\n",0,0,0);, 344 NSLCD_ACTION_GROUP_ALL, 345 (filter=cbp.mi->mi_filter,0) 346) 347