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