search.c 6.44 KB
Newer Older
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1
2
/* search.c - /etc/passwd backend search function */

Kurt Zeilenga's avatar
Kurt Zeilenga committed
3
4
#include "portable.h"

Kurt Zeilenga's avatar
Kurt Zeilenga committed
5
#include <stdio.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
6

7
#include <ac/ctype.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
8
9
10
11
#include <ac/socket.h>
#include <ac/string.h>
#include <ac/time.h>

Kurt Zeilenga's avatar
Kurt Zeilenga committed
12
#include <pwd.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
13

Kurt Zeilenga's avatar
Kurt Zeilenga committed
14
#include "slap.h"
15
#include "external.h"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
16

17
18
19
20
static Entry *pw2entry(
	Backend *be,
	struct passwd *pw,
	char *rdn);
Kurt Zeilenga's avatar
Kurt Zeilenga committed
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

int
passwd_back_search(
    Backend	*be,
    Connection	*conn,
    Operation	*op,
    char	*base,
    int		scope,
    int		deref,
    int		slimit,
    int		tlimit,
    Filter	*filter,
    char	*filterstr,
    char	**attrs,
    int		attrsonly
)
{
	struct passwd	*pw;
	Entry		*e;
	char		*s;
	time_t		stoptime;
42
43
44

	int sent = 0;
	int err = LDAP_SUCCESS;
45
46
47
48
49

	char *rdn = NULL;
	char *parent = NULL;
	char *matched = NULL;
	char *user = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
50
51
52
53
54
55
56

	tlimit = (tlimit > be->be_timelimit || tlimit < 1) ? be->be_timelimit
	    : tlimit;
	stoptime = op->o_time + tlimit;
	slimit = (slimit > be->be_sizelimit || slimit < 1) ? be->be_sizelimit
	    : slimit;

57
58
	endpwent();

Kurt Zeilenga's avatar
Kurt Zeilenga committed
59
60
61
62
63
64
#ifdef HAVE_SETPWFILE
	if ( be->be_private != NULL ) {
		(void) setpwfile( (char *) be->be_private );
	}
#endif /* HAVE_SETPWFILE */

65
66
67
68
69
70
	/* Handle a query for the base of this backend */
	if ( be_issuffix( be,  base ) ) {
		struct berval	val, *vals[2];

		vals[0] = &val;
		vals[1] = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
71

72
		matched = ch_strdup( base );
73

74
75
76
77
78
		if( scope != LDAP_SCOPE_ONELEVEL ) {
			/* Create an entry corresponding to the base DN */
			e = (Entry *) ch_calloc(1, sizeof(Entry));
			e->e_attrs = NULL;
			e->e_dn = ch_strdup( base );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
79

80
81
82
83
			/* Use the first attribute of the DN
		 	* as an attribute within the entry itself.
		 	*/
			rdn = dn_rdn(NULL, base);
84

85
86
87
88
			if( rdn == NULL || (s = strchr(rdn, '=')) == NULL ) {
				err = LDAP_INVALID_DN_SYNTAX;
				goto done;
			}
89

90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
			val.bv_val = rdn_attr_value(rdn);
			val.bv_len = strlen( val.bv_val );
			attr_merge( e, rdn_attr_type(rdn), vals );

			free(rdn);
			rdn = NULL;

			/* Every entry needs an objectclass. We don't really
			 * know if our hardcoded choice here agrees with the
			 * DN that was configured for this backend, but it's
			 * better than nothing.
			 *
			 * should be a configuratable item
			 */
			val.bv_val = "organizationalUnit";
			val.bv_len = strlen( val.bv_val );
			attr_merge( e, "objectClass", vals );
107
	
108
109
110
111
112
			if ( test_filter( be, conn, op, e, filter ) == 0 ) {
				send_search_entry( be, conn, op,
					e, attrs, attrsonly, 0, NULL );
				sent++;
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
113
114
		}

115
116
		if ( scope != LDAP_SCOPE_BASE ) {
			/* check all our "children" */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
117

118
119
120
121
122
123
124
125
126
127
128
129
130
			for ( pw = getpwent(); pw != NULL; pw = getpwent() ) {
				/* check for abandon */
				ldap_pvt_thread_mutex_lock( &op->o_abandonmutex );
				if ( op->o_abandon ) {
					ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
					endpwent();
					return( -1 );
				}
				ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );

				/* check time limit */
				if ( slap_get_time() > stoptime ) {
					send_ldap_result( conn, op, LDAP_TIMELIMIT_EXCEEDED,
131
			    		NULL, NULL, NULL, NULL );
132
133
134
135
136
					endpwent();
					return( 0 );
				}

				e = pw2entry( be, pw, NULL );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
137

138
139
140
141
				if ( test_filter( be, conn, op, e, filter ) == 0 ) {
					/* check size limit */
					if ( --slimit == -1 ) {
						send_ldap_result( conn, op, LDAP_SIZELIMIT_EXCEEDED,
142
				    		NULL, NULL, NULL, NULL );
143
144
145
146
						endpwent();
						return( 0 );
					}

147
148
					send_search_entry( be, conn, op,
						e, attrs, attrsonly, 0, NULL );
149
150
151
152
153
					sent++;
				}

				entry_free( e );
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
154
155
156
			endpwent();
		}

157
158
159
160
161
162
163
	} else {
		parent = dn_parent( be, base );

		/* This backend is only one layer deep. Don't answer requests for
		 * anything deeper than that.
		 */
		if( !be_issuffix( be, parent ) ) {
164
165
166
167
168
169
170
171
172
173
174
175
			int i;
			for( i=0; be->be_suffix[i] != NULL; i++ ) {
				if( dn_issuffix( base, be->be_suffix[i] ) ) {
					matched = ch_strdup( be->be_suffix[i] );
					break;
				}
			}
			err = LDAP_NO_SUCH_OBJECT;
			goto done;
		}

		if( scope == LDAP_SCOPE_ONELEVEL ) {
176
			goto done;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
177
178
		}

179
		rdn = dn_rdn( NULL, base );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
180

181
		if ( (user = rdn_attr_value(rdn)) == NULL) {
182
			err = LDAP_OPERATIONS_ERROR;
183
184
			goto done;
		}
185

186
187
188
189
190
		for( s = user; *s ; s++ ) {
			*s = TOLOWER( *s );
		}

		if ( (pw = getpwnam( user )) == NULL ) {
191
192
193
			matched = parent;
			parent = NULL;
			err = LDAP_NO_SUCH_OBJECT;
194
195
196
197
198
199
			goto done;
		}

		e = pw2entry( be, pw, rdn );

		if ( test_filter( be, conn, op, e, filter ) == 0 ) {
200
201
			send_search_entry( be, conn, op,
				e, attrs, attrsonly, 0, NULL );
202
			sent++;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
203
204
205
206
		}

		entry_free( e );
	}
207
208

done:
209
210
211
	send_ldap_result( conn, op,
		err, err == LDAP_NO_SUCH_OBJECT ? matched : NULL, NULL,
		NULL, NULL );
212
213
214
215
216

	if( matched != NULL ) free( matched );
	if( parent != NULL ) free( parent );
	if( rdn != NULL ) free( rdn );
	if( user != NULL ) free( user );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
217
218
219
220
221

	return( 0 );
}

static Entry *
222
pw2entry( Backend *be, struct passwd *pw, char *rdn )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
223
224
225
226
227
228
229
230
231
232
{
	Entry		*e;
	char		buf[256];
	struct berval	val;
	struct berval	*vals[2];

	vals[0] = &val;
	vals[1] = NULL;

	/*
233
234
	 * from pw we get pw_name and make it cn
	 * give it an objectclass of person.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
235
236
237
238
239
	 */

	e = (Entry *) ch_calloc( 1, sizeof(Entry) );
	e->e_attrs = NULL;

240
241
	/* rdn attribute type should be a configuratable item */
	sprintf( buf, "uid=%s,%s", pw->pw_name, be->be_suffix[0] );
242
	e->e_dn = ch_strdup( buf );
243
	e->e_ndn = dn_normalize_case( ch_strdup( buf ) );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
244
245
246

	val.bv_val = pw->pw_name;
	val.bv_len = strlen( pw->pw_name );
247
248
249
250
	attr_merge( e, "uid", vals );	/* required by uidObject */
	attr_merge( e, "cn", vals );	/* required by person */
	attr_merge( e, "sn", vals );	/* required by person */

Kurt Zeilenga's avatar
Kurt Zeilenga committed
251
#ifdef HAVE_PW_GECOS
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
	/*
	 * if gecos is present, add it as a cn. first process it
	 * according to standard BSD usage. If the processed cn has
	 * a space, use the tail as the surname.
	 */
	if (pw->pw_gecos[0]) {
		char *s;

		val.bv_val = pw->pw_gecos;
		val.bv_len = strlen(val.bv_val);
		attr_merge(e, "description", vals);

		s = strchr(val.bv_val, ',');
		if (s)
			*s = '\0';
		s = strchr(val.bv_val, '&');
		if (s) {
			int i = s - val.bv_val;
			strncpy(buf, val.bv_val, i);
			s = buf+i;
			strcpy(s, pw->pw_name);
			if (islower(*s))
				*s = toupper(*s);
			strcat(s, val.bv_val+i+1);
			val.bv_val = buf;
		}
		val.bv_len = strlen(val.bv_val);
		if ( strcmp( val.bv_val, pw->pw_name ))
			attr_merge( e, "cn", vals );
		if ( (s=strrchr(val.bv_val, ' '))) {
			val.bv_val = s + 1;
			val.bv_len = strlen(val.bv_val);
			attr_merge(e, "sn", vals);
		}
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
287
#endif
288
289

	/* objectclasses should be configuratable items */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
290
291
292
293
	val.bv_val = "person";
	val.bv_len = strlen( val.bv_val );
	attr_merge( e, "objectclass", vals );

294
295
296
	val.bv_val = "uidObject";
	val.bv_len = strlen( val.bv_val );
	attr_merge( e, "objectclass", vals );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
297
298
	return( e );
}