Home | History | Annotate | Line # | Download | only in nssov
passwd.c revision 1.1.1.10
      1 /*	$NetBSD: passwd.c,v 1.1.1.10 2025/09/05 21:09:45 christos Exp $	*/
      2 
      3 /* passwd.c - password lookup routines */
      4 /* $OpenLDAP$ */
      5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      6  *
      7  * Copyright 2008-2024 The OpenLDAP Foundation.
      8  * Portions Copyright 2008 by Howard Chu, Symas Corp.
      9  * All rights reserved.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted only as authorized by the OpenLDAP
     13  * Public License.
     14  *
     15  * A copy of this license is available in the file LICENSE in the
     16  * top-level directory of the distribution or, alternatively, at
     17  * <http://www.OpenLDAP.org/license.html>.
     18  */
     19 /* ACKNOWLEDGEMENTS:
     20  * This code references portions of the nss-ldapd package
     21  * written by Arthur de Jong. The nss-ldapd code was forked
     22  * from the nss-ldap library written by Luke Howard.
     23  */
     24 
     25 #include "nssov.h"
     26 
     27 /* ( nisSchema.2.0 NAME 'posixAccount' SUP top AUXILIARY
     28  *	 DESC 'Abstraction of an account with POSIX attributes'
     29  *	 MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
     30  *	 MAY ( userPassword $ loginShell $ gecos $ description ) )
     31  */
     32 
     33 /* the basic search filter for searches */
     34 static struct berval passwd_filter = BER_BVC("(objectClass=posixAccount)");
     35 
     36 /* the attributes used in searches */
     37 static struct berval passwd_keys[] = {
     38 	BER_BVC("uid"),
     39 	BER_BVC("userPassword"),
     40 	BER_BVC("uidNumber"),
     41 	BER_BVC("gidNumber"),
     42 	BER_BVC("gecos"),
     43 	BER_BVC("cn"),
     44 	BER_BVC("homeDirectory"),
     45 	BER_BVC("loginShell"),
     46 	BER_BVC("objectClass"),
     47 	BER_BVNULL
     48 };
     49 
     50 #define UID_KEY	0
     51 #define	PWD_KEY	1
     52 #define UIDN_KEY	2
     53 #define GIDN_KEY	3
     54 #define GEC_KEY	4
     55 #define CN_KEY	5
     56 #define DIR_KEY	6
     57 #define SHL_KEY	7
     58 
     59 /* default values for attributes */
     60 static struct berval default_passwd_userPassword	= BER_BVC("*"); /* unmatchable */
     61 static struct berval default_passwd_homeDirectory	= BER_BVC("");
     62 static struct berval default_passwd_loginShell		= BER_BVC("");
     63 
     64 static struct berval shadow_passwd = BER_BVC("x");
     65 
     66 NSSOV_INIT(passwd)
     67 
     68 /*
     69 	 Checks to see if the specified name is a valid user name.
     70 
     71 	 This test is based on the definition from POSIX (IEEE Std 1003.1, 2004, 3.426 User Name
     72 	 and 3.276 Portable Filename Character Set):
     73 	 http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_426
     74 	 http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_276
     75 
     76 	 The standard defines user names valid if they contain characters from
     77 	 the set [A-Za-z0-9._-] where the hyphen should not be used as first
     78 	 character. As an extension this test allows the dolar '$' sign as the last
     79 	 character to support Samba special accounts.
     80 */
     81 int isvalidusername(struct berval *bv)
     82 {
     83 	int i;
     84 	char *name = bv->bv_val;
     85 	if ((name==NULL)||(name[0]=='\0'))
     86 		return 0;
     87 	/* check first character */
     88 	if ( ! ( (name[0]>='A' && name[0] <= 'Z') ||
     89 					 (name[0]>='a' && name[0] <= 'z') ||
     90 					 (name[0]>='0' && name[0] <= '9') ||
     91 					 name[0]=='.' || name[0]=='_' ) )
     92 		return 0;
     93 	/* check other characters */
     94 	for (i=1;i<bv->bv_len;i++)
     95 	{
     96 		if ( name[i]=='$' )
     97 		{
     98 			/* if the char is $ we require it to be the last char */
     99 			if (name[i+1]!='\0')
    100 				return 0;
    101 		}
    102 		else if ( ! ( (name[i]>='A' && name[i] <= 'Z') ||
    103 									(name[i]>='a' && name[i] <= 'z') ||
    104 									(name[i]>='0' && name[i] <= '9') ||
    105 									name[i]=='.' || name[i]=='_'	|| name[i]=='-') )
    106 			return 0;
    107 	}
    108 	/* no test failed so it must be good */
    109 	return -1;
    110 }
    111 
    112 /* return 1 on success */
    113 int nssov_dn2uid(Operation *op,nssov_info *ni,struct berval *dn,struct berval *uid)
    114 {
    115 	nssov_mapinfo *mi = &ni->ni_maps[NM_passwd];
    116 	AttributeDescription *ad = mi->mi_attrs[UID_KEY].an_desc;
    117 	Entry *e;
    118 
    119 	/* check for empty string */
    120 	if (!dn->bv_len)
    121 		return 0;
    122 	/* try to look up uid within DN string */
    123 	if (!strncmp(dn->bv_val,ad->ad_cname.bv_val,ad->ad_cname.bv_len) &&
    124 		dn->bv_val[ad->ad_cname.bv_len] == '=')
    125 	{
    126 		struct berval bv, rdn;
    127 		dnRdn(dn, &rdn);
    128 		/* check if it is valid */
    129 		bv.bv_val = dn->bv_val + ad->ad_cname.bv_len + 1;
    130 		bv.bv_len = rdn.bv_len - ad->ad_cname.bv_len - 1;
    131 		if (!isvalidusername(&bv))
    132 			return 0;
    133 		ber_dupbv_x( uid, &bv, op->o_tmpmemctx );
    134 		return 1;
    135 	}
    136 	/* look up the uid from the entry itself */
    137 	if (be_entry_get_rw( op, dn, NULL, ad, 0, &e) == LDAP_SUCCESS)
    138 	{
    139 		Attribute *a = attr_find(e->e_attrs, ad);
    140 		if (a) {
    141 			ber_dupbv_x(uid, &a->a_vals[0], op->o_tmpmemctx);
    142 		}
    143 		be_entry_release_r(op, e);
    144 		if (a)
    145 			return 1;
    146 	}
    147 	return 0;
    148 }
    149 
    150 int nssov_name2dn_cb(Operation *op,SlapReply *rs)
    151 {
    152 	if ( rs->sr_type == REP_SEARCH )
    153 	{
    154 		struct berval *bv = op->o_callback->sc_private;
    155 		if ( !BER_BVISNULL(bv)) {
    156 			op->o_tmpfree( bv->bv_val, op->o_tmpmemctx );
    157 			BER_BVZERO(bv);
    158 			return LDAP_ALREADY_EXISTS;
    159 		}
    160 		ber_dupbv_x(bv, &rs->sr_entry->e_name, op->o_tmpmemctx);
    161 	}
    162 	return LDAP_SUCCESS;
    163 }
    164 
    165 int nssov_uid2dn(Operation *op,nssov_info *ni,struct berval *uid,struct berval *dn)
    166 {
    167 	nssov_mapinfo *mi = &ni->ni_maps[NM_passwd];
    168 	char fbuf[1024];
    169 	struct berval filter = {sizeof(fbuf),fbuf};
    170 	slap_callback cb = {0};
    171 	SlapReply rs = {REP_RESULT};
    172 	Operation op2;
    173 	int rc;
    174 
    175 	/* if it isn't a valid username, just bail out now */
    176 	if (!isvalidusername(uid))
    177 		return 0;
    178 	/* we have to look up the entry */
    179 	nssov_filter_byid(mi,UID_KEY,uid,&filter);
    180 	BER_BVZERO(dn);
    181 	cb.sc_private = dn;
    182 	cb.sc_response = nssov_name2dn_cb;
    183 	op2 = *op;
    184 	op2.o_callback = &cb;
    185 	op2.o_req_dn = mi->mi_base;
    186 	op2.o_req_ndn = mi->mi_base;
    187 	op2.ors_scope = mi->mi_scope;
    188 	op2.ors_filterstr = filter;
    189 	op2.ors_filter = str2filter_x( op, filter.bv_val );
    190 	op2.ors_attrs = slap_anlist_no_attrs;
    191 	op2.ors_tlimit = SLAP_NO_LIMIT;
    192 	op2.ors_slimit = SLAP_NO_LIMIT;
    193 	rc = op2.o_bd->be_search( &op2, &rs );
    194 	filter_free_x( op, op2.ors_filter, 1 );
    195 	return rc == LDAP_SUCCESS && !BER_BVISNULL(dn);
    196 }
    197 
    198 /* the maximum number of uidNumber attributes per entry */
    199 #define MAXUIDS_PER_ENTRY 5
    200 
    201 NSSOV_CBPRIV(passwd,
    202 	char buf[256];
    203 	struct berval name;
    204 	struct berval id;);
    205 
    206 static struct berval shadowclass = BER_BVC("shadowAccount");
    207 
    208 static int write_passwd(nssov_passwd_cbp *cbp,Entry *entry)
    209 {
    210 	int32_t tmpint32;
    211 	struct berval tmparr[2], tmpuid[2];
    212 	char *tmp;
    213 	struct berval *names;
    214 	struct berval *uids;
    215 	struct berval passwd = {0};
    216 	gid_t gid;
    217 	struct berval gecos;
    218 	struct berval homedir;
    219 	struct berval shell;
    220 	Attribute *a;
    221 	int i,j;
    222 	int use_shadow = 0;
    223 	/* get the usernames for this entry */
    224 	if (BER_BVISNULL(&cbp->name))
    225 	{
    226 		a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UID_KEY].an_desc);
    227 		if (!a)
    228 		{
    229 			Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
    230 				entry->e_name.bv_val, cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val );
    231 			return 0;
    232 		}
    233 		names = a->a_vals;
    234 	}
    235 	else
    236 	{
    237 		names=tmparr;
    238 		names[0]=cbp->name;
    239 		BER_BVZERO(&names[1]);
    240 	}
    241 	/* get the password for this entry */
    242 	a = attr_find(entry->e_attrs, slap_schema.si_ad_objectClass);
    243 	if ( a ) {
    244 		for ( i=0; i<a->a_numvals; i++) {
    245 			if ( bvmatch( &shadowclass, &a->a_nvals[i] )) {
    246 				use_shadow = 1;
    247 				break;
    248 			}
    249 		}
    250 	}
    251 	if ( use_shadow )
    252 	{
    253 		/* if the entry has a shadowAccount entry, point to that instead */
    254 		passwd = shadow_passwd;
    255 	}
    256 	else
    257 	{
    258 		a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[PWD_KEY].an_desc);
    259 		if (a)
    260 			get_userpassword(&a->a_vals[0], &passwd);
    261 		if (BER_BVISNULL(&passwd))
    262 			passwd=default_passwd_userPassword;
    263 	}
    264 	/* get the uids for this entry */
    265 	if (BER_BVISNULL(&cbp->id))
    266 	{
    267 		a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UIDN_KEY].an_desc);
    268         if ( !a )
    269 		{
    270 			Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
    271 				entry->e_name.bv_val, cbp->mi->mi_attrs[UIDN_KEY].an_desc->ad_cname.bv_val );
    272 			return 0;
    273 		}
    274 		uids = a->a_vals;
    275 	}
    276 	else
    277 	{
    278 		uids = tmpuid;
    279 		uids[0] = cbp->id;
    280 		BER_BVZERO(&uids[1]);
    281 	}
    282 	/* get the gid for this entry */
    283 	a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GIDN_KEY].an_desc);
    284 	if (!a)
    285 	{
    286 		Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
    287 			entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val );
    288 		return 0;
    289 	}
    290 	else if (a->a_numvals != 1)
    291 	{
    292 		Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n",
    293 			entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val );
    294 	}
    295 	gid=(gid_t)strtol(a->a_vals[0].bv_val,&tmp,0);
    296 	if ((a->a_vals[0].bv_val[0]=='\0')||(*tmp!='\0'))
    297 	{
    298 		Debug(LDAP_DEBUG_ANY,"passwd entry %s contains non-numeric %s value\n",
    299 			entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val );
    300 		return 0;
    301 	}
    302 	/* get the gecos for this entry (fall back to cn) */
    303 	a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GEC_KEY].an_desc);
    304 	if (!a)
    305 		a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[CN_KEY].an_desc);
    306 	if (!a || !a->a_numvals)
    307 	{
    308 		Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s or %s value\n",
    309 			entry->e_name.bv_val,
    310 			cbp->mi->mi_attrs[GEC_KEY].an_desc->ad_cname.bv_val,
    311 			cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val);
    312 		return 0;
    313 	}
    314 	else if (a->a_numvals > 1)
    315 	{
    316 		Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s or %s values\n",
    317 			entry->e_name.bv_val,
    318 			cbp->mi->mi_attrs[GEC_KEY].an_desc->ad_cname.bv_val,
    319 			cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val);
    320 	}
    321 	gecos=a->a_vals[0];
    322 	/* get the home directory for this entry */
    323 	a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[DIR_KEY].an_desc);
    324 	if (!a)
    325 	{
    326 		Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
    327 			entry->e_name.bv_val, cbp->mi->mi_attrs[DIR_KEY].an_desc->ad_cname.bv_val );
    328 		homedir=default_passwd_homeDirectory;
    329 	}
    330 	else
    331 	{
    332 		if (a->a_numvals > 1)
    333 		{
    334 			Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n",
    335 				entry->e_name.bv_val, cbp->mi->mi_attrs[DIR_KEY].an_desc->ad_cname.bv_val );
    336 		}
    337 		homedir=a->a_vals[0];
    338 		if (homedir.bv_val[0]=='\0')
    339 			homedir=default_passwd_homeDirectory;
    340 	}
    341 	/* get the shell for this entry */
    342 	a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[SHL_KEY].an_desc);
    343 	if (!a)
    344 	{
    345 		shell=default_passwd_loginShell;
    346 	}
    347 	else
    348 	{
    349 		if (a->a_numvals > 1)
    350 		{
    351 			Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n",
    352 				entry->e_name.bv_val, cbp->mi->mi_attrs[SHL_KEY].an_desc->ad_cname.bv_val );
    353 		}
    354 		shell=a->a_vals[0];
    355 		if (shell.bv_val[0]=='\0')
    356 			shell=default_passwd_loginShell;
    357 	}
    358 	/* write the entries */
    359 	for (i=0;!BER_BVISNULL(&names[i]);i++)
    360 	{
    361 		if (!isvalidusername(&names[i]))
    362 		{
    363 			Debug(LDAP_DEBUG_ANY,"nssov: passwd entry %s contains invalid user name: \"%s\"\n",
    364 				entry->e_name.bv_val,names[i].bv_val );
    365 		}
    366 		else
    367 		{
    368 			for (j=0;!BER_BVISNULL(&uids[j]);j++)
    369 			{
    370 				char *tmp;
    371 				uid_t uid;
    372 				uid = strtol(uids[j].bv_val, &tmp, 0);
    373 				if ( *tmp ) {
    374 					Debug(LDAP_DEBUG_ANY,"nssov: passwd entry %s contains non-numeric %s value: \"%s\"\n",
    375 						entry->e_name.bv_val, cbp->mi->mi_attrs[UIDN_KEY].an_desc->ad_cname.bv_val,
    376 						names[i].bv_val);
    377 					continue;
    378 				}
    379 				WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
    380 				WRITE_BERVAL(cbp->fp,&names[i]);
    381 				WRITE_BERVAL(cbp->fp,&passwd);
    382 				WRITE_INT32(cbp->fp,uid);
    383 				WRITE_INT32(cbp->fp,gid);
    384 				WRITE_BERVAL(cbp->fp,&gecos);
    385 				WRITE_BERVAL(cbp->fp,&homedir);
    386 				WRITE_BERVAL(cbp->fp,&shell);
    387 			}
    388 		}
    389 	}
    390 	return 0;
    391 }
    392 
    393 NSSOV_CB(passwd)
    394 
    395 NSSOV_HANDLE(
    396 	passwd,byname,
    397 	char fbuf[1024];
    398 	struct berval filter = {sizeof(fbuf)};
    399 	filter.bv_val = fbuf;
    400 	READ_STRING(fp,cbp.buf);
    401 	cbp.name.bv_len = tmpint32;
    402 	cbp.name.bv_val = cbp.buf;
    403 	if (!isvalidusername(&cbp.name)) {
    404 		Debug(LDAP_DEBUG_ANY,"nssov_passwd_byname(%s): invalid user name\n",cbp.name.bv_val);
    405 		return -1;
    406 	}
    407 	BER_BVZERO(&cbp.id); ,
    408 	Debug(LDAP_DEBUG_TRACE,"nssov_passwd_byname(%s)\n",cbp.name.bv_val);,
    409 	NSLCD_ACTION_PASSWD_BYNAME,
    410 	nssov_filter_byname(cbp.mi,UID_KEY,&cbp.name,&filter)
    411 )
    412 
    413 NSSOV_HANDLE(
    414 	passwd,byuid,
    415 	uid_t uid;
    416 	char fbuf[1024];
    417 	struct berval filter = {sizeof(fbuf)};
    418 	filter.bv_val = fbuf;
    419 	READ_INT32(fp,uid);
    420 	cbp.id.bv_val = cbp.buf;
    421 	cbp.id.bv_len = snprintf(cbp.buf,sizeof(cbp.buf),"%d",uid);
    422 	BER_BVZERO(&cbp.name);,
    423 	Debug(LDAP_DEBUG_TRACE,"nssov_passwd_byuid(%s)\n",cbp.id.bv_val);,
    424 	NSLCD_ACTION_PASSWD_BYUID,
    425 	nssov_filter_byid(cbp.mi,UIDN_KEY,&cbp.id,&filter)
    426 )
    427 
    428 NSSOV_HANDLE(
    429 	passwd,all,
    430 	struct berval filter;
    431 	/* no parameters to read */
    432 	BER_BVZERO(&cbp.name);
    433 	BER_BVZERO(&cbp.id);,
    434 	Debug(LDAP_DEBUG_TRACE,"nssov_passwd_all()\n");,
    435 	NSLCD_ACTION_PASSWD_ALL,
    436 	(filter=cbp.mi->mi_filter,0)
    437 )
    438