]> git.sur5r.net Git - openldap/blob - servers/slapd/passwd.c
2e083dc4bdbbd71e6b3a1c8d125d43e12d1eebab
[openldap] / servers / slapd / passwd.c
1 /* passwd.c - password extended operation routines */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2003 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16
17 #include "portable.h"
18
19 #include <stdio.h>
20
21 #include <ac/krb.h>
22 #include <ac/socket.h>
23 #include <ac/string.h>
24 #include <ac/unistd.h>
25
26 #include "slap.h"
27
28 #include <lber_pvt.h>
29 #include <lutil.h>
30
31 int passwd_extop(
32         Operation *op,
33         SlapReply *rs )
34 {
35         struct berval id = {0, NULL}, old = {0, NULL}, new = {0, NULL},
36                 dn, ndn, hash, vals[2];
37         int freenew = 0;
38         Modifications ml, **modtail;
39         Operation op2;
40         slap_callback cb = { slap_null_cb, NULL };
41
42         assert( ber_bvcmp( &slap_EXOP_MODIFY_PASSWD, &op->ore_reqoid ) == 0 );
43
44         if( op->o_dn.bv_len == 0 ) {
45                 rs->sr_text = "only authenticated users may change passwords";
46                 return LDAP_STRONG_AUTH_REQUIRED;
47         }
48
49         rs->sr_err = slap_passwd_parse( op->oq_extended.rs_reqdata, &id,
50                 &old, &new, &rs->sr_text );
51         if ( rs->sr_err != LDAP_SUCCESS ) {
52                 return rs->sr_err;
53         }
54
55         if ( id.bv_len ) {
56                 dn = id;
57                 /* ndn is in tmpmem, so we don't need to free it */
58                 rs->sr_err = dnNormalize( 0, NULL, NULL, &dn, &ndn, op->o_tmpmemctx );
59                 if ( rs->sr_err != LDAP_SUCCESS ) {
60                         rs->sr_text = "Invalid DN";
61                         return rs->sr_err;
62                 }
63                 op->o_bd = select_backend( &ndn, 0, 0 );
64         } else {
65                 dn = op->o_dn;
66                 ndn = op->o_ndn;
67                 ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
68                 op->o_bd = op->o_conn->c_authz_backend;
69                 ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
70         }
71
72         if ( ndn.bv_len == 0 ) {
73                 rs->sr_text = "no password is associated with the Root DSE";
74                 return LDAP_UNWILLING_TO_PERFORM;
75         }
76
77         if( op->o_bd && !op->o_bd->be_modify ) {
78                 rs->sr_text = "operation not supported for current user";
79                 return LDAP_UNWILLING_TO_PERFORM;
80         }
81
82         if (backend_check_restrictions( op, rs,
83                         (struct berval *)&slap_EXOP_MODIFY_PASSWD ) != LDAP_SUCCESS) {
84                 return rs->sr_err;
85         }
86
87         if( op->o_bd == NULL ) {
88 #ifdef HAVE_CYRUS_SASL
89                 return slap_sasl_setpass( op, rs );
90 #else
91                 rs->sr_text = "no authz backend";
92                 return LDAP_OTHER;
93 #endif
94         }
95
96 #ifndef SLAPD_MULTIMASTER
97         /* This does not apply to multi-master case */
98         if( op->o_bd->be_update_ndn.bv_len ) {
99                 /* we SHOULD return a referral in this case */
100                 BerVarray defref = NULL;
101                 if ( !LDAP_STAILQ_EMPTY( &op->o_bd->be_syncinfo )) {
102                         syncinfo_t *si;
103                         LDAP_STAILQ_FOREACH( si, &op->o_bd->be_syncinfo, si_next ) {
104                                 struct berval tmpbv;
105                                 ber_dupbv( &tmpbv, &si->si_provideruri_bv[0] );
106                                 ber_bvarray_add( &defref, &tmpbv );
107                         }
108                 } else {
109                         defref = referral_rewrite( op->o_bd->be_update_refs,
110                                 NULL, NULL, LDAP_SCOPE_DEFAULT );
111                 }
112                 rs->sr_ref = defref;
113                 return LDAP_REFERRAL;
114         }
115 #endif /* !SLAPD_MULTIMASTER */
116         if ( new.bv_len == 0 ) {
117                 slap_passwd_generate( &new );
118                 freenew = 1;
119         }
120         if ( new.bv_len == 0 ) {
121                 rs->sr_text = "password generation failed";
122                 return LDAP_OTHER;
123         }
124         slap_passwd_hash( &new, &hash, &rs->sr_text );
125         if ( freenew ) {
126                 free( new.bv_val );
127         }
128         if ( hash.bv_len == 0 ) {
129                 if ( !rs->sr_text ) {
130                         rs->sr_text = "password hash failed";
131                 }
132                 return LDAP_OTHER;
133         }
134         vals[0] = hash;
135         vals[1].bv_val = NULL;
136         ml.sml_desc = slap_schema.si_ad_userPassword;
137         ml.sml_values = vals;
138         ml.sml_nvalues = NULL;
139         ml.sml_op = LDAP_MOD_REPLACE;
140         ml.sml_next = NULL;
141
142         op2 = *op;
143         op2.o_tag = LDAP_REQ_MODIFY;
144         op2.o_callback = &cb;
145         op2.o_req_dn = dn;
146         op2.o_req_ndn = ndn;
147         op2.orm_modlist = &ml;
148
149         modtail = &ml.sml_next;
150         rs->sr_err = slap_mods_opattrs( &op2, &ml, modtail, &rs->sr_text,
151                 NULL, 0 );
152         
153         if ( rs->sr_err == LDAP_SUCCESS ) {
154                 rs->sr_err = op2.o_bd->be_modify( &op2, rs );
155                 if ( rs->sr_err == LDAP_SUCCESS ) {
156                         replog( &op2 );
157                 }
158         }
159         slap_mods_free( ml.sml_next );
160         free( hash.bv_val );
161
162         return rs->sr_err;
163 }
164
165 int slap_passwd_parse( struct berval *reqdata,
166         struct berval *id,
167         struct berval *oldpass,
168         struct berval *newpass,
169         const char **text )
170 {
171         int rc = LDAP_SUCCESS;
172         ber_tag_t tag;
173         ber_len_t len;
174         BerElementBuffer berbuf;
175         BerElement *ber = (BerElement *)&berbuf;
176
177         if( reqdata == NULL ) {
178                 return LDAP_SUCCESS;
179         }
180
181         if( reqdata->bv_len == 0 ) {
182                 *text = "empty request data field";
183                 return LDAP_PROTOCOL_ERROR;
184         }
185
186         /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
187         ber_init2( ber, reqdata, 0 );
188
189         tag = ber_scanf( ber, "{" /*}*/ );
190
191         if( tag != LBER_ERROR ) {
192                 tag = ber_peek_tag( ber, &len );
193         }
194
195         if( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_ID ) {
196                 if( id == NULL ) {
197 #ifdef NEW_LOGGING
198                         LDAP_LOG( OPERATION, ERR,
199                            "slap_passwd_parse: ID not allowed.\n", 0, 0, 0 );
200 #else
201                         Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: ID not allowed.\n",
202                                 0, 0, 0 );
203 #endif
204
205                         *text = "user must change own password";
206                         rc = LDAP_UNWILLING_TO_PERFORM;
207                         goto done;
208                 }
209
210                 tag = ber_scanf( ber, "m", id );
211
212                 if( tag == LBER_ERROR ) {
213 #ifdef NEW_LOGGING
214                         LDAP_LOG( OPERATION, ERR,
215                            "slap_passwd_parse:  ID parse failed.\n", 0, 0, 0 );
216 #else
217                         Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: ID parse failed.\n",
218                                 0, 0, 0 );
219 #endif
220
221                         goto decoding_error;
222                 }
223
224                 tag = ber_peek_tag( ber, &len);
225         }
226
227         if( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_OLD ) {
228                 if( oldpass == NULL ) {
229 #ifdef NEW_LOGGING
230                         LDAP_LOG( OPERATION, ERR,
231                            "slap_passwd_parse: OLD not allowed.\n" , 0, 0, 0 );
232 #else
233                         Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: OLD not allowed.\n",
234                                 0, 0, 0 );
235 #endif
236
237                         *text = "use bind to verify old password";
238                         rc = LDAP_UNWILLING_TO_PERFORM;
239                         goto done;
240                 }
241
242                 tag = ber_scanf( ber, "m", oldpass );
243
244                 if( tag == LBER_ERROR ) {
245 #ifdef NEW_LOGGING
246                         LDAP_LOG( OPERATION, ERR,
247                            "slap_passwd_parse:  ID parse failed.\n" , 0, 0, 0 );
248 #else
249                         Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: ID parse failed.\n",
250                                 0, 0, 0 );
251 #endif
252
253                         goto decoding_error;
254                 }
255
256                 tag = ber_peek_tag( ber, &len );
257         }
258
259         if( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_NEW ) {
260                 if( newpass == NULL ) {
261 #ifdef NEW_LOGGING
262                         LDAP_LOG( OPERATION, ERR,
263                            "slap_passwd_parse:  NEW not allowed.\n", 0, 0, 0 );
264 #else
265                         Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: NEW not allowed.\n",
266                                 0, 0, 0 );
267 #endif
268
269                         *text = "user specified passwords disallowed";
270                         rc = LDAP_UNWILLING_TO_PERFORM;
271                         goto done;
272                 }
273
274                 tag = ber_scanf( ber, "m", newpass );
275
276                 if( tag == LBER_ERROR ) {
277 #ifdef NEW_LOGGING
278                         LDAP_LOG( OPERATION, ERR,
279                            "slap_passwd_parse:  OLD parse failed.\n", 0, 0, 0 );
280 #else
281                         Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: OLD parse failed.\n",
282                                 0, 0, 0 );
283 #endif
284
285                         goto decoding_error;
286                 }
287
288                 tag = ber_peek_tag( ber, &len );
289         }
290
291         if( len != 0 ) {
292 decoding_error:
293 #ifdef NEW_LOGGING
294                 LDAP_LOG( OPERATION, ERR, 
295                         "slap_passwd_parse: decoding error, len=%ld\n", (long)len, 0, 0 );
296 #else
297                 Debug( LDAP_DEBUG_TRACE,
298                         "slap_passwd_parse: decoding error, len=%ld\n",
299                         (long) len, 0, 0 );
300 #endif
301
302                 *text = "data decoding error";
303                 rc = LDAP_PROTOCOL_ERROR;
304         }
305
306 done:
307         return rc;
308 }
309
310 struct berval * slap_passwd_return(
311         struct berval           *cred )
312 {
313         int rc;
314         struct berval *bv = NULL;
315         BerElementBuffer berbuf;
316         /* opaque structure, size unknown but smaller than berbuf */
317         BerElement *ber = (BerElement *)&berbuf;
318
319         assert( cred != NULL );
320
321 #ifdef NEW_LOGGING
322         LDAP_LOG( OPERATION, ENTRY, 
323                 "slap_passwd_return: %ld\n",(long)cred->bv_len, 0, 0 );
324 #else
325         Debug( LDAP_DEBUG_TRACE, "slap_passwd_return: %ld\n",
326                 (long) cred->bv_len, 0, 0 );
327 #endif
328         
329         ber_init_w_nullc( ber, LBER_USE_DER );
330
331         rc = ber_printf( ber, "{tON}",
332                 LDAP_TAG_EXOP_MODIFY_PASSWD_GEN, cred );
333
334         if( rc >= 0 ) {
335                 (void) ber_flatten( ber, &bv );
336         }
337
338         ber_free_buf( ber );
339
340         return bv;
341 }
342
343 int
344 slap_passwd_check(
345         Connection *conn,
346         Attribute *a,
347         struct berval *cred,
348         const char **text )
349 {
350         int result = 1;
351         struct berval *bv;
352
353 #if defined( SLAPD_CRYPT ) || defined( SLAPD_SPASSWD )
354         ldap_pvt_thread_mutex_lock( &passwd_mutex );
355 #ifdef SLAPD_SPASSWD
356         lutil_passwd_sasl_conn = conn->c_sasl_authctx;
357 #endif
358 #endif
359
360         for ( bv = a->a_vals; bv->bv_val != NULL; bv++ ) {
361                 if( !lutil_passwd( bv, cred, NULL, text ) ) {
362                         result = 0;
363                         break;
364                 }
365         }
366
367 #if defined( SLAPD_CRYPT ) || defined( SLAPD_SPASSWD )
368 #ifdef SLAPD_SPASSWD
369         lutil_passwd_sasl_conn = NULL;
370 #endif
371         ldap_pvt_thread_mutex_unlock( &passwd_mutex );
372 #endif
373
374         return result;
375 }
376
377 void
378 slap_passwd_generate( struct berval *pass )
379 {
380         struct berval *tmp;
381 #ifdef NEW_LOGGING
382         LDAP_LOG( OPERATION, ENTRY, "slap_passwd_generate: begin\n", 0, 0, 0 );
383 #else
384         Debug( LDAP_DEBUG_TRACE, "slap_passwd_generate\n", 0, 0, 0 );
385 #endif
386         /*
387          * generate passwords of only 8 characters as some getpass(3)
388          * implementations truncate at 8 characters.
389          */
390         tmp = lutil_passwd_generate( 8 );
391         if (tmp) {
392                 *pass = *tmp;
393                 free(tmp);
394         } else {
395                 pass->bv_val = NULL;
396                 pass->bv_len = 0;
397         }
398 }
399
400 void
401 slap_passwd_hash(
402         struct berval * cred,
403         struct berval * new,
404         const char **text )
405 {
406         struct berval *tmp;
407 #ifdef LUTIL_SHA1_BYTES
408         char* hash = default_passwd_hash ?  default_passwd_hash : "{SSHA}";
409 #else
410         char* hash = default_passwd_hash ?  default_passwd_hash : "{SMD5}";
411 #endif
412         
413
414 #if defined( SLAPD_CRYPT ) || defined( SLAPD_SPASSWD )
415         ldap_pvt_thread_mutex_lock( &passwd_mutex );
416 #endif
417
418         tmp = lutil_passwd_hash( cred , hash, text );
419         
420 #if defined( SLAPD_CRYPT ) || defined( SLAPD_SPASSWD )
421         ldap_pvt_thread_mutex_unlock( &passwd_mutex );
422 #endif
423
424         if( tmp == NULL ) {
425                 new->bv_len = 0;
426                 new->bv_val = NULL;
427         }
428
429         *new = *tmp;
430         free( tmp );
431         return;
432 }