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