/* group.c - group lookup routines */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software .
*
* Copyright 2008-2024 The OpenLDAP Foundation.
* Portions Copyright 2008-2009 by Howard Chu, Symas Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in the file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* .
*/
/* ACKNOWLEDGEMENTS:
* This code references portions of the nss-ldapd package
* written by Arthur de Jong. The nss-ldapd code was forked
* from the nss-ldap library written by Luke Howard.
*/
#include "nssov.h"
/* for gid_t */
#include
/* ( nisSchema.2.2 NAME 'posixGroup' SUP top STRUCTURAL
* DESC 'Abstraction of a group of accounts'
* MUST ( cn $ gidNumber )
* MAY ( userPassword $ memberUid $ description ) )
*
* apart from that the above the uniqueMember attributes may be
* supported in a coming release (they map to DNs, which is an extra
* lookup step)
*
* using nested groups (groups that are member of a group) is currently
* not supported, this may be added in a later release
*/
/* the basic search filter for searches */
static struct berval group_filter = BER_BVC("(objectClass=posixGroup)");
/* the attributes to request with searches */
static struct berval group_keys[] = {
BER_BVC("cn"),
BER_BVC("userPassword"),
BER_BVC("gidNumber"),
BER_BVC("memberUid"),
BER_BVC("uniqueMember"),
BER_BVNULL
};
#define CN_KEY 0
#define PWD_KEY 1
#define GID_KEY 2
#define UID_KEY 3
#define MEM_KEY 4
/* default values for attributes */
static struct berval default_group_userPassword = BER_BVC("*"); /* unmatchable */
NSSOV_CBPRIV(group,
nssov_info *ni;
char buf[256];
struct berval name;
struct berval gidnum;
struct berval user;
int wantmembers;);
/* create a search filter for searching a group entry
by member uid, return -1 on errors */
static int mkfilter_group_bymember(nssov_group_cbp *cbp,struct berval *buf)
{
struct berval dn;
/* try to translate uid to DN */
nssov_uid2dn(cbp->op,cbp->ni,&cbp->user,&dn);
if (BER_BVISNULL(&dn)) {
if (cbp->user.bv_len + cbp->mi->mi_filter.bv_len + cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_len + 6 >
buf->bv_len )
return -1;
buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))",
cbp->mi->mi_filter.bv_val, cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val,
cbp->user.bv_val );
} else { /* also lookup using user DN */
if (cbp->user.bv_len + cbp->mi->mi_filter.bv_len + cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_len +
dn.bv_len + cbp->mi->mi_attrs[MEM_KEY].an_desc->ad_cname.bv_len + 12 > buf->bv_len )
return -1;
buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(|(%s=%s)(%s=%s)))",
cbp->mi->mi_filter.bv_val,
cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val, cbp->user.bv_val,
cbp->mi->mi_attrs[MEM_KEY].an_desc->ad_cname.bv_val, dn.bv_val );
}
return 0;
}
NSSOV_INIT(group)
/*
Checks to see if the specified name is a valid group name.
This test is based on the definition from POSIX (IEEE Std 1003.1, 2004,
3.189 Group Name and 3.276 Portable Filename Character Set):
http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_189
http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_276
The standard defines group names valid if they only contain characters from
the set [A-Za-z0-9._-] where the hyphen should not be used as first
character.
*/
static int isvalidgroupname(struct berval *name)
{
int i;
if ( !name->bv_val || !name->bv_len )
return 0;
/* check first character */
if ( ! ( (name->bv_val[0]>='A' && name->bv_val[0] <= 'Z') ||
(name->bv_val[0]>='a' && name->bv_val[0] <= 'z') ||
(name->bv_val[0]>='0' && name->bv_val[0] <= '9') ||
name->bv_val[0]=='.' || name->bv_val[0]=='_' ) )
return 0;
/* check other characters */
for (i=1;ibv_len;i++)
{
#ifndef STRICT_GROUPS
/* allow spaces too */
if (name->bv_val[i] == ' ') continue;
#endif
if ( ! ( (name->bv_val[i]>='A' && name->bv_val[i] <= 'Z') ||
(name->bv_val[i]>='a' && name->bv_val[i] <= 'z') ||
(name->bv_val[i]>='0' && name->bv_val[i] <= '9') ||
name->bv_val[i]=='.' || name->bv_val[i]=='_' || name->bv_val[i]=='-') )
return 0;
}
/* no test failed so it must be good */
return -1;
}
static int write_group(nssov_group_cbp *cbp,Entry *entry)
{
struct berval tmparr[2], tmpgid[2];
struct berval *names,*gids,*members;
struct berval passwd = {0};
Attribute *a;
int i,j,nummembers,rc = 0;
/* get group name (cn) */
if (BER_BVISNULL(&cbp->name))
{
a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[CN_KEY].an_desc);
if ( !a )
{
Debug(LDAP_DEBUG_ANY,"group entry %s does not contain %s value\n",
entry->e_name.bv_val, cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val );
return 0;
}
names = a->a_vals;
}
else
{
names=tmparr;
names[0]=cbp->name;
BER_BVZERO(&names[1]);
}
/* get the group id(s) */
if (BER_BVISNULL(&cbp->gidnum))
{
a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GID_KEY].an_desc);
if ( !a )
{
Debug(LDAP_DEBUG_ANY,"group entry %s does not contain %s value\n",
entry->e_name.bv_val, cbp->mi->mi_attrs[GID_KEY].an_desc->ad_cname.bv_val );
return 0;
}
gids = a->a_vals;
}
else
{
gids=tmpgid;
gids[0]=cbp->gidnum;
BER_BVZERO(&gids[1]);
}
/* get group passwd (userPassword) (use only first entry) */
a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[PWD_KEY].an_desc);
if (a)
get_userpassword(&a->a_vals[0], &passwd);
if (BER_BVISNULL(&passwd))
passwd=default_group_userPassword;
/* get group members (memberUid&uniqueMember) */
if (cbp->wantmembers) {
Attribute *b;
i = 0; j = 0;
a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UID_KEY].an_desc);
b = attr_find(entry->e_attrs, cbp->mi->mi_attrs[MEM_KEY].an_desc);
if ( a )
i += a->a_numvals;
if ( b )
i += b->a_numvals;
if ( i ) {
members = cbp->op->o_tmpalloc( (i+1) * sizeof(struct berval), cbp->op->o_tmpmemctx );
if ( a ) {
for (i=0; ia_numvals; i++) {
if (isvalidusername(&a->a_vals[i])) {
ber_dupbv_x(&members[j],&a->a_vals[i],cbp->op->o_tmpmemctx);
j++;
}
}
}
a = b;
if ( a ) {
for (i=0; ia_numvals; i++) {
if (nssov_dn2uid(cbp->op,cbp->ni,&a->a_nvals[i],&members[j]))
j++;
}
}
nummembers = j;
BER_BVZERO(&members[j]);
} else {
members=NULL;
nummembers = 0;
}
} else {
members=NULL;
nummembers = 0;
}
/* write entries for all names and gids */
for (i=0;!BER_BVISNULL(&names[i]);i++)
{
if (!isvalidgroupname(&names[i]))
{
Debug(LDAP_DEBUG_ANY,"nssov: group entry %s contains invalid group name: \"%s\"\n",
entry->e_name.bv_val,names[i].bv_val );
}
else
{
for (j=0;!BER_BVISNULL(&gids[j]);j++)
{
char *tmp;
int tmpint32;
gid_t gid;
gid = strtol(gids[j].bv_val, &tmp, 0);
if ( *tmp ) {
Debug(LDAP_DEBUG_ANY,"nssov: group entry %s contains non-numeric %s value: \"%s\"\n",
entry->e_name.bv_val, cbp->mi->mi_attrs[GID_KEY].an_desc->ad_cname.bv_val,
names[i].bv_val);
continue;
}
WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
WRITE_BERVAL(cbp->fp,&names[i]);
WRITE_BERVAL(cbp->fp,&passwd);
WRITE_INT32(cbp->fp,gid);
/* write a list of values */
WRITE_INT32(cbp->fp,nummembers);
if (nummembers)
{
int k;
for (k=0;kfp,&members[k]);
}
}
}
}
}
/* free and return */
if (members!=NULL)
ber_bvarray_free_x( members, cbp->op->o_tmpmemctx );
return rc;
}
NSSOV_CB(group)
NSSOV_HANDLE(
group,byname,
char fbuf[1024];
struct berval filter = {sizeof(fbuf)};
filter.bv_val = fbuf;
READ_STRING(fp,cbp.buf);
cbp.name.bv_len = tmpint32;
cbp.name.bv_val = cbp.buf;
if (!isvalidgroupname(&cbp.name)) {
Debug(LDAP_DEBUG_ANY,"nssov_group_byname(%s): invalid group name\n",cbp.name.bv_val);
return -1;
}
cbp.wantmembers = 1;
cbp.ni = ni;
BER_BVZERO(&cbp.gidnum);
BER_BVZERO(&cbp.user);,
Debug(LDAP_DEBUG_TRACE,"nslcd_group_byname(%s)\n",cbp.name.bv_val);,
NSLCD_ACTION_GROUP_BYNAME,
nssov_filter_byname(cbp.mi,CN_KEY,&cbp.name,&filter)
)
NSSOV_HANDLE(
group,bygid,
gid_t gid;
char fbuf[1024];
struct berval filter = {sizeof(fbuf)};
filter.bv_val = fbuf;
READ_INT32(fp,gid);
cbp.gidnum.bv_val = cbp.buf;
cbp.gidnum.bv_len = snprintf(cbp.buf,sizeof(cbp.buf),"%d",gid);
cbp.wantmembers = 1;
cbp.ni = ni;
BER_BVZERO(&cbp.name);
BER_BVZERO(&cbp.user);,
Debug(LDAP_DEBUG_TRACE,"nssov_group_bygid(%s)\n",cbp.gidnum.bv_val);,
NSLCD_ACTION_GROUP_BYGID,
nssov_filter_byid(cbp.mi,GID_KEY,&cbp.gidnum,&filter)
)
NSSOV_HANDLE(
group,bymember,
char fbuf[1024];
struct berval filter = {sizeof(fbuf)};
filter.bv_val = fbuf;
READ_STRING(fp,cbp.buf);
cbp.user.bv_len = tmpint32;
cbp.user.bv_val = cbp.buf;
if (!isvalidusername(&cbp.user)) {
Debug(LDAP_DEBUG_ANY,"nssov_group_bymember(%s): invalid user name\n",cbp.user.bv_val);
return -1;
}
cbp.wantmembers = 0;
cbp.ni = ni;
BER_BVZERO(&cbp.name);
BER_BVZERO(&cbp.gidnum);,
Debug(LDAP_DEBUG_TRACE,"nssov_group_bymember(%s)\n",cbp.user.bv_val);,
NSLCD_ACTION_GROUP_BYMEMBER,
mkfilter_group_bymember(&cbp,&filter)
)
NSSOV_HANDLE(
group,all,
struct berval filter;
/* no parameters to read */
cbp.wantmembers = 1;
cbp.ni = ni;
BER_BVZERO(&cbp.name);
BER_BVZERO(&cbp.gidnum);,
Debug(LDAP_DEBUG_TRACE,"nssov_group_all()\n");,
NSLCD_ACTION_GROUP_ALL,
(filter=cbp.mi->mi_filter,0)
)