~ubuntu-branches/debian/experimental/389-ds-base/experimental

« back to all changes in this revision

Viewing changes to .pc/fix-typos.patch/ldap/servers/slapd/passwd_extop.c

  • Committer: Package Import Robot
  • Author(s): Timo Aaltonen
  • Date: 2014-07-08 15:50:11 UTC
  • mfrom: (0.2.2)
  • Revision ID: package-import@ubuntu.com-20140708155011-r66lvtioamqwaype
Tags: 1.3.2.19-1
* New upstream release.
* admin_scripts.diff: Updated to fix more bashisms.
* watch: Update the url.
* Install failedbinds.py and logregex.py scripts.
* init: Use status from init-functions.
* control: Update my email.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/** BEGIN COPYRIGHT BLOCK
 
2
 * This Program is free software; you can redistribute it and/or modify it under
 
3
 * the terms of the GNU General Public License as published by the Free Software
 
4
 * Foundation; version 2 of the License.
 
5
 * 
 
6
 * This Program is distributed in the hope that it will be useful, but WITHOUT
 
7
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 
8
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 
9
 * 
 
10
 * You should have received a copy of the GNU General Public License along with
 
11
 * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
 
12
 * Place, Suite 330, Boston, MA 02111-1307 USA.
 
13
 * 
 
14
 * In addition, as a special exception, Red Hat, Inc. gives You the additional
 
15
 * right to link the code of this Program with code not covered under the GNU
 
16
 * General Public License ("Non-GPL Code") and to distribute linked combinations
 
17
 * including the two, subject to the limitations in this paragraph. Non-GPL Code
 
18
 * permitted under this exception must only link to the code of this Program
 
19
 * through those well defined interfaces identified in the file named EXCEPTION
 
20
 * found in the source code files (the "Approved Interfaces"). The files of
 
21
 * Non-GPL Code may instantiate templates or use macros or inline functions from
 
22
 * the Approved Interfaces without causing the resulting work to be covered by
 
23
 * the GNU General Public License. Only Red Hat, Inc. may make changes or
 
24
 * additions to the list of Approved Interfaces. You must obey the GNU General
 
25
 * Public License in all respects for all of the Program code and other code used
 
26
 * in conjunction with the Program except the Non-GPL Code covered by this
 
27
 * exception. If you modify this file, you may extend this exception to your
 
28
 * version of the file, but you are not obligated to do so. If you do not wish to
 
29
 * provide this exception without modification, you must delete this exception
 
30
 * statement from your version and license this file solely under the GPL without
 
31
 * exception. 
 
32
 * 
 
33
 * 
 
34
 * Copyright (C) 2005 Red Hat, Inc.
 
35
 * All rights reserved.
 
36
 * END COPYRIGHT BLOCK **/
 
37
 
 
38
#ifdef HAVE_CONFIG_H
 
39
#  include <config.h>
 
40
#endif
 
41
 
 
42
/*
 
43
 * Password Modify - LDAP Extended Operation.
 
44
 * RFC 3062
 
45
 *
 
46
 *
 
47
 * This plugin implements the "Password Modify - LDAP3" 
 
48
 * extended operation for LDAP. The plugin function is called by
 
49
 * the server if an LDAP client request contains the OID:
 
50
 * "1.3.6.1.4.1.4203.1.11.1".
 
51
 *
 
52
 */
 
53
 
 
54
#include <stdio.h>
 
55
#include <string.h>
 
56
#include <private/pprio.h>
 
57
 
 
58
 
 
59
#include <prio.h>
 
60
#include <plbase64.h>
 
61
 
 
62
#include <ssl.h>
 
63
#include "slap.h"
 
64
#include "slapi-plugin.h"
 
65
#include "fe.h"
 
66
 
 
67
/* Type of connection for this operation;*/
 
68
#define LDAP_EXTOP_PASSMOD_CONN_SECURE
 
69
 
 
70
/* Uncomment the following line FOR TESTING: allows non-SSL connections to use the password change extended op */
 
71
/* #undef LDAP_EXTOP_PASSMOD_CONN_SECURE */
 
72
 
 
73
/* ber tags for the PasswdModifyRequestValue sequence */
 
74
#define LDAP_EXTOP_PASSMOD_TAG_USERID   0x80U
 
75
#define LDAP_EXTOP_PASSMOD_TAG_OLDPWD   0x81U
 
76
#define LDAP_EXTOP_PASSMOD_TAG_NEWPWD   0x82U
 
77
 
 
78
/* ber tags for the PasswdModifyResponseValue sequence */
 
79
#define LDAP_EXTOP_PASSMOD_TAG_GENPWD   0x80U
 
80
 
 
81
/* number of bytes used for random password generation */
 
82
#define LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN 8
 
83
 
 
84
/* number of random bytes needed to generate password */
 
85
#define LDAP_EXTOP_PASSMOD_RANDOM_BYTES 6
 
86
 
 
87
 
 
88
Slapi_PluginDesc passwdopdesc = { "passwd_modify_plugin", VENDOR, DS_PACKAGE_VERSION,
 
89
        "Password Modify extended operation plugin" };
 
90
 
 
91
/* Check SLAPI_USERPWD_ATTR attribute of the directory entry 
 
92
 * return 0, if the userpassword attribute contains the given pwd value
 
93
 * return -1, if userPassword attribute is absent for given Entry
 
94
 * return LDAP_INVALID_CREDENTIALS,if userPassword attribute and given pwd don't match
 
95
 */
 
96
static int passwd_check_pwd(Slapi_Entry *targetEntry, const char *pwd){
 
97
        int rc = LDAP_SUCCESS;
 
98
        Slapi_Attr *attr = NULL;
 
99
        Slapi_Value cv;
 
100
        Slapi_Value **bvals; 
 
101
 
 
102
        LDAPDebug( LDAP_DEBUG_TRACE, "=> passwd_check_pwd\n", 0, 0, 0 );
 
103
        
 
104
        slapi_value_init_string(&cv,pwd);
 
105
        
 
106
        if ( (rc = slapi_entry_attr_find( targetEntry, SLAPI_USERPWD_ATTR, &attr )) == 0 )
 
107
        { /* we have found the userPassword attribute and it has some value */
 
108
                bvals = attr_get_present_values( attr );
 
109
                if ( slapi_pw_find_sv( bvals, &cv ) != 0 )
 
110
                {
 
111
                        rc = LDAP_INVALID_CREDENTIALS;
 
112
                }
 
113
        }
 
114
 
 
115
        value_done(&cv);
 
116
        LDAPDebug( LDAP_DEBUG_TRACE, "<= passwd_check_pwd: %d\n", rc, 0, 0 );
 
117
        
 
118
        /* if the userPassword attribute is absent then rc is -1 */
 
119
        return rc;
 
120
}
 
121
 
 
122
 
 
123
/* Searches the dn in directory, 
 
124
 *  If found     : fills in slapi_entry structure and returns 0
 
125
 *  If NOT found : returns the search result as LDAP_NO_SUCH_OBJECT
 
126
 */
 
127
static int 
 
128
passwd_modify_getEntry( const char *dn, Slapi_Entry **e2 ) {
 
129
        int             search_result = 0;
 
130
        Slapi_DN        sdn;
 
131
        LDAPDebug( LDAP_DEBUG_TRACE, "=> passwd_modify_getEntry\n", 0, 0, 0 );
 
132
        slapi_sdn_init_dn_byref( &sdn, dn );
 
133
        if ((search_result = slapi_search_internal_get_entry( &sdn, NULL, e2,
 
134
                                        plugin_get_default_component_id())) != LDAP_SUCCESS ){
 
135
         LDAPDebug (LDAP_DEBUG_TRACE, "passwd_modify_getEntry: No such entry-(%s), err (%d)\n",
 
136
                                         dn, search_result, 0);
 
137
        }
 
138
 
 
139
        slapi_sdn_done( &sdn );
 
140
        LDAPDebug( LDAP_DEBUG_TRACE, "<= passwd_modify_getEntry: %d\n", search_result, 0, 0 );
 
141
        return search_result;
 
142
}
 
143
 
 
144
 
 
145
/* Construct Mods pblock and perform the modify operation 
 
146
 * Sets result of operation in SLAPI_PLUGIN_INTOP_RESULT 
 
147
 */
 
148
static int 
 
149
passwd_apply_mods(Slapi_PBlock *pb_orig, const Slapi_DN *sdn, Slapi_Mods *mods,
 
150
        LDAPControl **req_controls, LDAPControl ***resp_controls) 
 
151
{
 
152
        Slapi_PBlock pb;
 
153
        LDAPControl **req_controls_copy = NULL;
 
154
        LDAPControl **pb_resp_controls = NULL;
 
155
        int ret=0;
 
156
 
 
157
        LDAPDebug( LDAP_DEBUG_TRACE, "=> passwd_apply_mods\n", 0, 0, 0 );
 
158
 
 
159
        if (mods && (slapi_mods_get_num_mods(mods) > 0)) 
 
160
        {
 
161
                /* We need to dup the request controls since the original
 
162
                 * pblock owns the ones that have been passed in. */
 
163
                if (req_controls) {
 
164
                        slapi_add_controls(&req_controls_copy, req_controls, 1);
 
165
                }
 
166
 
 
167
                pblock_init(&pb);
 
168
                slapi_modify_internal_set_pb_ext (&pb, sdn, 
 
169
                        slapi_mods_get_ldapmods_byref(mods),
 
170
                        req_controls_copy, NULL, /* UniqueID */
 
171
                        plugin_get_default_component_id(), /* PluginID */
 
172
                        0); /* Flags */ 
 
173
 
 
174
                /* We copy the connection from the original pblock into the
 
175
                 * pblock we use for the internal modify operation.  We do
 
176
                 * this to allow the password policy code to be able to tell
 
177
                 * that the password change was initiated by the user who
 
178
                 * sent the extended operation instead of always assuming
 
179
                 * that it was done by the root DN. */
 
180
                pb.pb_conn = pb_orig->pb_conn;
 
181
 
 
182
                ret =slapi_modify_internal_pb (&pb);
 
183
 
 
184
                /* We now clean up the connection that we copied into the
 
185
                 * new pblock.  We want to leave it untouched. */
 
186
                pb.pb_conn = NULL;
 
187
  
 
188
                slapi_pblock_get(&pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
 
189
 
 
190
                /* Retreive and duplicate the response controls since they will be
 
191
                 * destroyed along with the pblock used for the internal operation. */
 
192
                slapi_pblock_get(&pb, SLAPI_RESCONTROLS, &pb_resp_controls);
 
193
                if (pb_resp_controls) {
 
194
                        slapi_add_controls(resp_controls, pb_resp_controls, 1);
 
195
                }
 
196
 
 
197
                if (ret != LDAP_SUCCESS){
 
198
                        LDAPDebug(LDAP_DEBUG_TRACE, "WARNING: passwordPolicy modify error %d on entry '%s'\n",
 
199
                                ret, slapi_sdn_get_dn(sdn), 0);
 
200
                }
 
201
 
 
202
                pblock_done(&pb);
 
203
        }
 
204
 
 
205
        LDAPDebug( LDAP_DEBUG_TRACE, "<= passwd_apply_mods: %d\n", ret, 0, 0 );
 
206
 
 
207
        return ret;
 
208
}
 
209
 
 
210
 
 
211
 
 
212
/* Modify the userPassword attribute field of the entry */
 
213
static int passwd_modify_userpassword(Slapi_PBlock *pb_orig, Slapi_Entry *targetEntry,
 
214
        const char *newPasswd, LDAPControl **req_controls, LDAPControl ***resp_controls)
 
215
{
 
216
        int ret = 0;
 
217
        Slapi_Mods smods;
 
218
        
 
219
    LDAPDebug( LDAP_DEBUG_TRACE, "=> passwd_modify_userpassword\n", 0, 0, 0 );
 
220
        
 
221
        slapi_mods_init (&smods, 0);
 
222
        slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, SLAPI_USERPWD_ATTR, newPasswd);
 
223
 
 
224
 
 
225
        ret = passwd_apply_mods(pb_orig, slapi_entry_get_sdn_const(targetEntry),
 
226
                                &smods, req_controls, resp_controls);
 
227
 
 
228
        slapi_mods_done(&smods);
 
229
        
 
230
    LDAPDebug( LDAP_DEBUG_TRACE, "<= passwd_modify_userpassword: %d\n", ret, 0, 0 );
 
231
 
 
232
        return ret;
 
233
}
 
234
 
 
235
/* Generate a new, basic random password */
 
236
static int passwd_modify_generate_basic_passwd( int passlen, char **genpasswd )
 
237
{
 
238
        char *data = NULL;
 
239
        char *enc = NULL;
 
240
        int datalen = LDAP_EXTOP_PASSMOD_RANDOM_BYTES;
 
241
 
 
242
        if ( genpasswd == NULL ) {
 
243
                return LDAP_OPERATIONS_ERROR;
 
244
        }
 
245
 
 
246
        if ( passlen > 0 ) {
 
247
                datalen = passlen * 3 / 4 + 1;
 
248
        }
 
249
 
 
250
        data = slapi_ch_calloc( datalen, 1 );
 
251
 
 
252
        /* get random bytes from NSS */
 
253
        PK11_GenerateRandom( (unsigned char *)data, datalen );
 
254
 
 
255
        /* b64 encode the random bytes to get a password made up
 
256
         * of printable characters. */
 
257
        enc = PL_Base64Encode( data, datalen, NULL );
 
258
 
 
259
        /* This will get freed by the caller */
 
260
        *genpasswd = slapi_ch_malloc( 1 + passlen );
 
261
 
 
262
        /* trim the password to the proper length */
 
263
        PL_strncpyz( *genpasswd, enc, passlen + 1 );
 
264
 
 
265
        slapi_ch_free_string( &data );
 
266
        slapi_ch_free_string( &enc );
 
267
 
 
268
        return LDAP_SUCCESS;
 
269
}
 
270
 
 
271
/* Generate a new, password-policy-based random password */
 
272
static int passwd_modify_generate_policy_passwd( passwdPolicy *pwpolicy,
 
273
                                                                                        char **genpasswd, char **errMesg )
 
274
{
 
275
        unsigned char *data = NULL;
 
276
        int passlen = 0;
 
277
        int tmplen = 0;
 
278
        enum {
 
279
                idx_minuppers = 0,
 
280
                idx_minlowers,
 
281
                idx_mindigits,
 
282
                idx_minspecials,
 
283
                idx_end
 
284
        }; 
 
285
        int my_policy[idx_end];
 
286
        struct {
 
287
                int chr_start;
 
288
                int chr_range;
 
289
        } chr_table[] = { /* NOTE: the above enum order */
 
290
                { 65, 26 }, /* [ A - Z ] */
 
291
                { 97, 26 }, /* [ a - z ] */
 
292
                { 48, 10 }, /* [ 0 - 9 ] */
 
293
                { 58,  7 }  /* [ : - @ ] */
 
294
        };
 
295
#define gen_policy_pw_getchar(n, idx) \
 
296
( chr_table[(idx)].chr_start + (n) % chr_table[(idx)].chr_range )
 
297
        int i;
 
298
 
 
299
        if ( genpasswd == NULL ) {
 
300
                return LDAP_OPERATIONS_ERROR;
 
301
        }
 
302
 
 
303
        my_policy[idx_mindigits] = pwpolicy->pw_mindigits;
 
304
        my_policy[idx_minuppers] = pwpolicy->pw_minuppers;
 
305
        my_policy[idx_minlowers] = pwpolicy->pw_minlowers;
 
306
        my_policy[idx_minspecials] = pwpolicy->pw_minspecials;
 
307
 
 
308
        /* if only minalphas is set, divide it into minuppers and minlowers. */
 
309
        if ( pwpolicy->pw_minalphas > 0 &&
 
310
                 ( my_policy[idx_minuppers] == 0 && my_policy[idx_minlowers] == 0 )) {
 
311
                unsigned int x = (unsigned int)time(NULL);
 
312
                my_policy[idx_minuppers] = slapi_rand_r(&x) % pwpolicy->pw_minalphas;
 
313
                my_policy[idx_minlowers] = pwpolicy->pw_minalphas - my_policy[idx_minuppers];
 
314
        }
 
315
 
 
316
        if ( pwpolicy->pw_mincategories ) {
 
317
                int categories = 0;
 
318
                for ( i = 0; i < idx_end; i++ ) {
 
319
                        if ( my_policy[i] > 0 ) {
 
320
                                categories++;
 
321
                        }
 
322
                }
 
323
                if ( pwpolicy->pw_mincategories > categories ) {
 
324
                        categories = pwpolicy->pw_mincategories;
 
325
                        for ( i = 0; i < idx_end; i++ ) {
 
326
                                if ( my_policy[i] == 0 ) {
 
327
                                        /* force to add a policy to match the pw_mincategories */
 
328
                                        my_policy[i] = 1; 
 
329
                                }
 
330
                                if ( --categories == 0 ) {
 
331
                                        break;
 
332
                                }
 
333
                        }
 
334
                        if ( categories > 0 ) {
 
335
                                /* password generator does not support passwordMin8Bit */
 
336
                        LDAPDebug( LDAP_DEBUG_ANY,
 
337
                                        "Unable to generate a password that meets the current "
 
338
                                        "password syntax rules.  A minimum categories setting "
 
339
                                        "of %d is not supported with random password generation.\n",
 
340
                                        pwpolicy->pw_mincategories, 0, 0 );
 
341
                                *errMesg = "Unable to generate new random password.  Please contact the Administrator.";
 
342
                                return LDAP_CONSTRAINT_VIOLATION;
 
343
                        }
 
344
                }
 
345
        }
 
346
 
 
347
        /* get the password length */
 
348
        tmplen = 0;
 
349
        for ( i = 0; i < idx_end; i++ ) {
 
350
                tmplen += my_policy[i];
 
351
        }
 
352
        passlen = tmplen;
 
353
        if ( passlen < pwpolicy->pw_minlength ) {
 
354
                passlen = pwpolicy->pw_minlength;
 
355
        }
 
356
        if ( passlen < LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN ) {
 
357
                passlen = LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN;
 
358
        }
 
359
 
 
360
        data = (unsigned char *)slapi_ch_calloc( passlen, 1 );
 
361
 
 
362
        /* get random bytes from NSS */
 
363
        PK11_GenerateRandom( data, passlen );
 
364
 
 
365
        /* if password length is longer the sum of my_policy's,
 
366
           let them share the burden */
 
367
        if ( passlen > tmplen ) {
 
368
                unsigned int x = (unsigned int)time(NULL);
 
369
                int delta = passlen - tmplen;
 
370
                for ( i = 0; i < delta; i++ ) {
 
371
                        my_policy[(x = slapi_rand_r(&x)) % idx_end]++;
 
372
                }
 
373
        }
 
374
 
 
375
        /* This will get freed by the caller */
 
376
        *genpasswd = slapi_ch_malloc( 1 + passlen );
 
377
 
 
378
        for ( i = 0; i < passlen; i++ ) {
 
379
                int idx = data[i] % idx_end;
 
380
                int isfirst = 1;
 
381
                /* choose a category based on the random value */
 
382
                while ( my_policy[idx] <= 0 ) {
 
383
                        if ( ++idx == idx_end ) {
 
384
                                idx = 0; /* if no rule is found, default is uppercase */
 
385
                                if ( !isfirst ) {
 
386
                                        break;
 
387
                                }
 
388
                                isfirst = 0;
 
389
                        }
 
390
                }
 
391
                my_policy[idx]--;
 
392
                (*genpasswd)[i] = gen_policy_pw_getchar(data[i], idx);
 
393
        }
 
394
        (*genpasswd)[passlen] = '\0';
 
395
 
 
396
        slapi_ch_free( (void **)&data );
 
397
 
 
398
        return LDAP_SUCCESS;
 
399
}
 
400
 
 
401
/* Generate a new random password */
 
402
static int passwd_modify_generate_passwd( passwdPolicy *pwpolicy,
 
403
                                                                                  char **genpasswd, char **errMesg )
 
404
{
 
405
        int minalphalen = 0;
 
406
        int passlen = LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN;
 
407
        int rval = LDAP_SUCCESS;
 
408
 
 
409
        if ( genpasswd == NULL ) {
 
410
                return LDAP_OPERATIONS_ERROR;
 
411
        }
 
412
        if ( pwpolicy->pw_min8bit > 0 ) {
 
413
        LDAPDebug( LDAP_DEBUG_ANY, "Unable to generate a password that meets "
 
414
                                                "the current password syntax rules.  8-bit syntax "
 
415
                                                "restrictions are not supported with random password "
 
416
                                                "generation.\n", 0, 0, 0 );
 
417
                *errMesg = "Unable to generate new random password.  Please contact the Administrator.";
 
418
                return LDAP_CONSTRAINT_VIOLATION;
 
419
        }
 
420
 
 
421
        if ( pwpolicy->pw_minalphas || pwpolicy->pw_minuppers || 
 
422
                 pwpolicy->pw_minlowers || pwpolicy->pw_mindigits ||
 
423
                 pwpolicy->pw_minspecials || pwpolicy->pw_maxrepeats ||
 
424
                 pwpolicy->pw_mincategories > 2 ) {
 
425
                rval = passwd_modify_generate_policy_passwd( pwpolicy, genpasswd,
 
426
                                                                                                         errMesg );
 
427
        } else {
 
428
                /* find out the minimum length to fulfill the passwd policy 
 
429
                   requirements */
 
430
                minalphalen = pwpolicy->pw_minuppers + pwpolicy->pw_minlowers;
 
431
                if ( minalphalen < pwpolicy->pw_minalphas ) {
 
432
                        minalphalen = pwpolicy->pw_minalphas;
 
433
                }
 
434
                passlen = minalphalen + pwpolicy->pw_mindigits + 
 
435
                                  pwpolicy->pw_minspecials + pwpolicy->pw_min8bit;
 
436
                if ( passlen < pwpolicy->pw_minlength ) {
 
437
                        passlen = pwpolicy->pw_minlength;
 
438
                }
 
439
                if ( passlen < LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN ) {
 
440
                        passlen = LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN;
 
441
                }
 
442
                rval = passwd_modify_generate_basic_passwd( passlen, genpasswd );
 
443
        }
 
444
 
 
445
        return rval;
 
446
}
 
447
 
 
448
 
 
449
/* Password Modify Extended operation plugin function */
 
450
int
 
451
passwd_modify_extop( Slapi_PBlock *pb )
 
452
{
 
453
        char            *oid = NULL;
 
454
        char            *bindDN = NULL;
 
455
        Slapi_DN        *bindSDN = NULL;
 
456
        char            *authmethod = NULL;
 
457
        char            *rawdn = NULL;
 
458
        const char      *dn = NULL;
 
459
        char            *otdn = NULL;
 
460
        char            *oldPasswd = NULL;
 
461
        char            *newPasswd = NULL;
 
462
        char            *errMesg = NULL;
 
463
        int             ret=0, rc=0, sasl_ssf=0, local_ssf=0, need_pwpolicy_ctrl=0;
 
464
        ber_tag_t       tag=0;
 
465
        ber_len_t       len=(ber_len_t)-1;
 
466
        struct berval   *extop_value = NULL;
 
467
        struct berval   *gen_passwd = NULL;
 
468
        BerElement      *ber = NULL;
 
469
        BerElement      *response_ber = NULL;
 
470
        Slapi_Entry     *targetEntry=NULL;
 
471
        Connection      *conn = NULL;
 
472
        LDAPControl     **req_controls = NULL;
 
473
        LDAPControl     **resp_controls = NULL;
 
474
        passwdPolicy    *pwpolicy = NULL;
 
475
        Slapi_DN        *target_sdn = NULL;
 
476
        Slapi_Entry     *referrals = NULL;
 
477
        /* Slapi_DN sdn; */
 
478
 
 
479
        LDAPDebug( LDAP_DEBUG_TRACE, "=> passwd_modify_extop\n", 0, 0, 0 );
 
480
 
 
481
        /* Before going any further, we'll make sure that the right extended operation plugin
 
482
         * has been called: i.e., the OID shipped whithin the extended operation request must 
 
483
         * match this very plugin's OID: EXTOP_PASSWD_OID. */
 
484
        if ( slapi_pblock_get( pb, SLAPI_EXT_OP_REQ_OID, &oid ) != 0 ) {
 
485
                errMesg = "Could not get OID value from request.\n";
 
486
                rc = LDAP_OPERATIONS_ERROR;
 
487
                slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_extop", 
 
488
                                "%s", errMesg );
 
489
                goto free_and_return;
 
490
        } else {
 
491
                slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_extop", 
 
492
                                 "Received extended operation request with OID %s\n", oid );
 
493
        }
 
494
        
 
495
        if ( strcasecmp( oid, EXTOP_PASSWD_OID ) != 0) {
 
496
                errMesg = "Request OID does not match Passwd OID.\n";
 
497
                rc = LDAP_OPERATIONS_ERROR;
 
498
                goto free_and_return;
 
499
        } else {
 
500
                slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_extop", 
 
501
                                 "Password Modify extended operation request confirmed.\n" );
 
502
        }
 
503
        
 
504
        /* Now , at least we know that the request was indeed a Password Modify one. */
 
505
 
 
506
#ifdef LDAP_EXTOP_PASSMOD_CONN_SECURE
 
507
        /* Allow password modify only for SSL/TLS established connections and
 
508
         * connections using SASL privacy layers */
 
509
        conn = pb->pb_conn;
 
510
        if ( slapi_pblock_get(pb, SLAPI_CONN_SASL_SSF, &sasl_ssf) != 0) {
 
511
                errMesg = "Could not get SASL SSF from connection\n";
 
512
                rc = LDAP_OPERATIONS_ERROR;
 
513
                slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_extop",
 
514
                                "%s", errMesg );
 
515
                goto free_and_return;
 
516
        }
 
517
 
 
518
        if ( slapi_pblock_get(pb, SLAPI_CONN_LOCAL_SSF, &local_ssf) != 0) {
 
519
                errMesg = "Could not get local SSF from connection\n";
 
520
                rc = LDAP_OPERATIONS_ERROR;
 
521
                slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_extop",
 
522
                                "%s", errMesg );
 
523
                goto free_and_return;
 
524
        }
 
525
 
 
526
        if ( ((conn->c_flags & CONN_FLAG_SSL) != CONN_FLAG_SSL) &&
 
527
              (sasl_ssf <= 1) && (local_ssf <= 1)) {
 
528
                errMesg = "Operation requires a secure connection.\n";
 
529
                rc = LDAP_CONFIDENTIALITY_REQUIRED;
 
530
                goto free_and_return;
 
531
        }
 
532
#endif
 
533
 
 
534
        /* Get the ber value of the extended operation */
 
535
        slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_VALUE, &extop_value);
 
536
 
 
537
        if (!BV_HAS_DATA(extop_value))
 
538
        {
 
539
                /* The request field wasn't provided.  We'll
 
540
                 * now try to determine the userid and verify
 
541
                 * knowledge of the old password via other
 
542
                 * means.
 
543
                 */
 
544
                goto parse_req_done;
 
545
        }
 
546
        
 
547
        if ((ber = ber_init(extop_value)) == NULL)
 
548
        {
 
549
                errMesg = "PasswdModify Request decode failed.\n";
 
550
                rc = LDAP_PROTOCOL_ERROR;
 
551
                goto free_and_return;
 
552
        }
 
553
 
 
554
        /* Format of request to parse
 
555
         *
 
556
         * PasswdModifyRequestValue ::= SEQUENCE {
 
557
         * userIdentity    [0]  OCTET STRING OPTIONAL
 
558
         * oldPasswd       [1]  OCTET STRING OPTIONAL
 
559
         * newPasswd       [2]  OCTET STRING OPTIONAL }
 
560
         *
 
561
         * The request value field is optional. If it is
 
562
         * provided, at least one field must be filled in.
 
563
         */
 
564
 
 
565
        /* ber parse code */
 
566
        if ( ber_scanf( ber, "{") == LBER_ERROR )
 
567
        {
 
568
                /* The request field wasn't provided.  We'll
 
569
                 * now try to determine the userid and verify
 
570
                 * knowledge of the old password via other
 
571
                 * means.
 
572
                 */
 
573
                goto parse_req_done;
 
574
        } else {
 
575
                tag = ber_peek_tag( ber, &len);
 
576
        }
 
577
 
 
578
        
 
579
        /* identify userID field by tags */
 
580
        if (tag == LDAP_EXTOP_PASSMOD_TAG_USERID )
 
581
        {
 
582
                int rc = 0;
 
583
                if ( ber_scanf( ber, "a", &rawdn) == LBER_ERROR ) {
 
584
                        slapi_ch_free_string(&rawdn);
 
585
                        LDAPDebug( LDAP_DEBUG_ANY, "ber_scanf failed :{\n", 0, 0, 0 );
 
586
                        errMesg = "ber_scanf failed at userID parse.\n";
 
587
                        rc = LDAP_PROTOCOL_ERROR;
 
588
                        goto free_and_return;
 
589
                }
 
590
 
 
591
                /* Check if we should be performing strict validation. */
 
592
                if (config_get_dn_validate_strict()) {
 
593
                        /* check that the dn is formatted correctly */
 
594
                        rc = slapi_dn_syntax_check(pb, rawdn, 1);
 
595
                        if (rc) { /* syntax check failed */
 
596
                                op_shared_log_error_access(pb, "EXT", rawdn?rawdn:"",
 
597
                                                                "strict: invalid target dn");
 
598
                                errMesg = "invalid target dn.\n";
 
599
                                slapi_ch_free_string(&rawdn);
 
600
                                rc = LDAP_INVALID_SYNTAX;
 
601
                                goto free_and_return;
 
602
                        }
 
603
                }
 
604
                tag = ber_peek_tag(ber, &len);
 
605
        } 
 
606
        
 
607
        /* identify oldPasswd field by tags */
 
608
        if (tag == LDAP_EXTOP_PASSMOD_TAG_OLDPWD ) {
 
609
                if ( ber_scanf( ber, "a", &oldPasswd ) == LBER_ERROR ) {
 
610
                        slapi_ch_free_string(&oldPasswd);
 
611
                        LDAPDebug( LDAP_DEBUG_ANY, "ber_scanf failed :{\n", 0, 0, 0 );
 
612
                        errMesg = "ber_scanf failed at oldPasswd parse.\n";
 
613
                        rc = LDAP_PROTOCOL_ERROR;
 
614
                        goto free_and_return;
 
615
                }
 
616
                tag = ber_peek_tag( ber, &len);
 
617
        }
 
618
        
 
619
        /* identify newPasswd field by tags */
 
620
        if (tag ==  LDAP_EXTOP_PASSMOD_TAG_NEWPWD )
 
621
        {
 
622
                if ( ber_scanf( ber, "a", &newPasswd ) == LBER_ERROR ) {
 
623
                        slapi_ch_free_string(&newPasswd);
 
624
                        LDAPDebug( LDAP_DEBUG_ANY, "ber_scanf failed :{\n", 0, 0, 0 );
 
625
                        errMesg = "ber_scanf failed at newPasswd parse.\n";
 
626
                        rc = LDAP_PROTOCOL_ERROR;
 
627
                        goto free_and_return;
 
628
                }
 
629
        }
 
630
 
 
631
parse_req_done: 
 
632
        /* Uncomment for debugging, otherwise we don't want to leak the password values into the log... */
 
633
        /* LDAPDebug( LDAP_DEBUG_ARGS, "passwd: dn (%s), oldPasswd (%s) ,newPasswd (%s)\n",
 
634
                                         dn, oldPasswd, newPasswd); */
 
635
 
 
636
        /* Get Bind DN */
 
637
        slapi_pblock_get( pb, SLAPI_CONN_DN, &bindDN );
 
638
 
 
639
        /* If the connection is bound anonymously, we must refuse to process this operation. */
 
640
        if (bindDN == NULL || *bindDN == '\0') {
 
641
                /* Refuse the operation because they're bound anonymously */
 
642
                errMesg = "Anonymous Binds are not allowed.\n";
 
643
                rc = LDAP_INSUFFICIENT_ACCESS;
 
644
                goto free_and_return;
 
645
        }
 
646
 
 
647
        /* Find and set the target DN. */
 
648
        if (rawdn && *rawdn != '\0') {
 
649
            target_sdn = slapi_sdn_new_dn_passin(rawdn);
 
650
        } else { /* We already checked (bindDN && *bindDN != '\0') above  */
 
651
            target_sdn = bindSDN = slapi_sdn_new_normdn_byref(bindDN);
 
652
        }
 
653
        dn = slapi_sdn_get_ndn(target_sdn);
 
654
        if (dn == NULL || *dn == '\0') {
 
655
                /* Refuse the operation because they're bound anonymously */
 
656
                errMesg = "Invalid dn.\n";
 
657
                rc = LDAP_INVALID_DN_SYNTAX;
 
658
                goto free_and_return;
 
659
        }
 
660
        slapi_pblock_set(pb, SLAPI_TARGET_SDN, target_sdn);
 
661
 
 
662
        /* Check if we need to send any referrals. */
 
663
        if (slapi_dn_write_needs_referral(target_sdn, &referrals)) {
 
664
                rc = LDAP_REFERRAL;
 
665
                goto free_and_return;
 
666
        }
 
667
 
 
668
        if (oldPasswd == NULL || *oldPasswd == '\0') {
 
669
                /* If user is authenticated, they already gave their password during
 
670
                 * the bind operation (or used sasl or client cert auth or OS creds) */
 
671
                slapi_pblock_get(pb, SLAPI_CONN_AUTHMETHOD, &authmethod);
 
672
                if (!authmethod || !strcmp(authmethod, SLAPD_AUTH_NONE)) {
 
673
                        errMesg = "User must be authenticated to the directory server.\n";
 
674
                        rc = LDAP_INSUFFICIENT_ACCESS;
 
675
                        goto free_and_return;
 
676
                }
 
677
        }
 
678
 
 
679
        /* Fetch the password policy.  We need this in case we need to
 
680
         * generate a password as well as for some policy checks. */
 
681
        pwpolicy = new_passwdPolicy( pb, dn );
 
682
         
 
683
        /* A new password was not supplied in the request, so we need to generate
 
684
         * a random one and return it to the user in a response.
 
685
         */
 
686
        if (newPasswd == NULL || *newPasswd == '\0') {
 
687
                int rval;
 
688
                /* Do a free of newPasswd here to be safe, otherwise we may leak 1 byte */
 
689
                slapi_ch_free_string( &newPasswd );
 
690
 
 
691
                /* Generate a new password */
 
692
                rval = passwd_modify_generate_passwd( pwpolicy, &newPasswd, &errMesg );
 
693
 
 
694
                if (rval != LDAP_SUCCESS) {
 
695
                        if (!errMesg)
 
696
                                errMesg = "Error generating new password.\n";
 
697
                        rc = LDAP_OPERATIONS_ERROR;
 
698
                        goto free_and_return;
 
699
                }
 
700
 
 
701
                /* Make sure a passwd was actually generated */
 
702
                if (newPasswd == NULL || *newPasswd == '\0') {
 
703
                        errMesg = "Error generating new password.\n";
 
704
                        rc = LDAP_OPERATIONS_ERROR;
 
705
                        goto free_and_return;
 
706
                }
 
707
 
 
708
                /*
 
709
                 * Create the response message since we generated a new password.
 
710
                 *
 
711
                 * PasswdModifyResponseValue ::= SEQUENCE {
 
712
                 *     genPasswd       [0]     OCTET STRING OPTIONAL }
 
713
                 */
 
714
                if ( (response_ber = ber_alloc()) == NULL ) {
 
715
                        rc = LDAP_NO_MEMORY;
 
716
                        goto free_and_return;
 
717
                }
 
718
 
 
719
                if ( LBER_ERROR == ( ber_printf( response_ber, "{ts}",
 
720
                                LDAP_EXTOP_PASSMOD_TAG_GENPWD, newPasswd ) ) ) {
 
721
                        ber_free( response_ber, 1 );
 
722
                        rc = LDAP_ENCODING_ERROR;
 
723
                        goto free_and_return;
 
724
                }
 
725
 
 
726
                ber_flatten(response_ber, &gen_passwd);
 
727
 
 
728
                /* We're done with response_ber now, so free it */
 
729
                ber_free( response_ber, 1 );
 
730
         }
 
731
         
 
732
         slapi_pblock_set( pb, SLAPI_ORIGINAL_TARGET, (void *)dn ); 
 
733
 
 
734
         /* Now we have the DN, look for the entry */
 
735
         ret = passwd_modify_getEntry(dn, &targetEntry);
 
736
         /* If we can't find the entry, then that's an error */
 
737
         if (ret) {
 
738
                /* Couldn't find the entry, fail */
 
739
                errMesg = "No such Entry exists.\n" ;
 
740
                rc = LDAP_NO_SUCH_OBJECT ;
 
741
                goto free_and_return;
 
742
         }
 
743
         
 
744
         /* First thing to do is to ask access control if the bound identity has
 
745
            rights to modify the userpassword attribute on this entry. If not, then
 
746
                we fail immediately with insufficient access. This means that we don't
 
747
                leak any useful information to the client such as current password
 
748
                wrong, etc.
 
749
          */
 
750
 
 
751
        operation_set_target_spec (pb->pb_op, slapi_entry_get_sdn(targetEntry));
 
752
        slapi_pblock_set( pb, SLAPI_REQUESTOR_ISROOT, &pb->pb_op->o_isroot );
 
753
 
 
754
        /* In order to perform the access control check , we need to select a backend (even though
 
755
         * we don't actually need it otherwise).
 
756
         */
 
757
        {
 
758
                Slapi_Backend *be = NULL;
 
759
 
 
760
                be = slapi_mapping_tree_find_backend_for_sdn(slapi_entry_get_sdn(targetEntry));
 
761
                if (NULL == be) {
 
762
                        errMesg = "Failed to find backend for target entry";
 
763
                        rc = LDAP_OPERATIONS_ERROR;
 
764
                        goto free_and_return;
 
765
                }
 
766
                slapi_pblock_set(pb, SLAPI_BACKEND, be);
 
767
        }
 
768
 
 
769
        /* Check if the pwpolicy control is present */
 
770
        slapi_pblock_get( pb, SLAPI_PWPOLICY, &need_pwpolicy_ctrl );
 
771
 
 
772
        ret = slapi_access_allowed ( pb, targetEntry, SLAPI_USERPWD_ATTR, NULL, SLAPI_ACL_WRITE );
 
773
        if ( ret != LDAP_SUCCESS ) {
 
774
                if (need_pwpolicy_ctrl) {
 
775
                        slapi_pwpolicy_make_response_control ( pb, -1, -1, LDAP_PWPOLICY_PWDMODNOTALLOWED );
 
776
                }
 
777
                errMesg = "Insufficient access rights\n";
 
778
                rc = LDAP_INSUFFICIENT_ACCESS;
 
779
                goto free_and_return;   
 
780
        }
 
781
                         
 
782
        /* Now we have the entry which we want to modify
 
783
         * They gave us a password (old), check it against the target entry
 
784
         * Is the old password valid ?
 
785
         */
 
786
        if (oldPasswd && *oldPasswd) {
 
787
                ret = passwd_check_pwd(targetEntry, oldPasswd);
 
788
                if (ret) {
 
789
                        /* No, then we fail this operation */
 
790
                        errMesg = "Invalid oldPasswd value.\n";
 
791
                        rc = ret;
 
792
                        goto free_and_return;
 
793
                }
 
794
        }
 
795
 
 
796
        /* Check if password policy allows users to change their passwords.  We need to do
 
797
         * this here since the normal modify code doesn't perform this check for
 
798
         * internal operations. */
 
799
        if (!pb->pb_op->o_isroot && !pb->pb_conn->c_needpw && !pwpolicy->pw_change) {
 
800
                if (NULL == bindSDN) {
 
801
                        bindSDN = slapi_sdn_new_normdn_byref(bindDN);
 
802
                }
 
803
                /* Is this a user modifying their own password? */
 
804
                if (slapi_sdn_compare(bindSDN, slapi_entry_get_sdn(targetEntry))==0) {
 
805
                        if (need_pwpolicy_ctrl) {
 
806
                                slapi_pwpolicy_make_response_control ( pb, -1, -1, LDAP_PWPOLICY_PWDMODNOTALLOWED );
 
807
                        }
 
808
                        errMesg = "User is not allowed to change password\n";
 
809
                        rc = LDAP_UNWILLING_TO_PERFORM;
 
810
                        goto free_and_return;
 
811
                }
 
812
        }
 
813
 
 
814
        /* Fetch any present request controls so we can use them when
 
815
         * performing the modify operation. */
 
816
        slapi_pblock_get(pb, SLAPI_REQCONTROLS, &req_controls);
 
817
        
 
818
        /* Now we're ready to make actual password change */
 
819
        ret = passwd_modify_userpassword(pb, targetEntry, newPasswd, req_controls, &resp_controls);
 
820
 
 
821
        /* Set the response controls if necessary.  We want to do this now
 
822
         * so it is set for both the success and failure cases.  The pblock
 
823
         * will now own the controls. */
 
824
        if (resp_controls) {
 
825
                slapi_pblock_set(pb, SLAPI_RESCONTROLS, resp_controls);
 
826
        }
 
827
 
 
828
        if (ret != LDAP_SUCCESS) {
 
829
                /* Failed to modify the password, e.g. because password policy, etc. */
 
830
                errMesg = "Failed to update password\n";
 
831
                rc = ret;
 
832
                goto free_and_return;
 
833
        }
 
834
 
 
835
        if (gen_passwd && (gen_passwd->bv_val != '\0')) {
 
836
                /* Set the reponse to let the user know the generated password */
 
837
                slapi_pblock_set(pb, SLAPI_EXT_OP_RET_VALUE, gen_passwd);
 
838
        }
 
839
        
 
840
        LDAPDebug( LDAP_DEBUG_TRACE, "<= passwd_modify_extop: %d\n", rc, 0, 0 );
 
841
        
 
842
        /* Free anything that we allocated above */
 
843
free_and_return:
 
844
        slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_extop",
 
845
                "%s", errMesg ? errMesg : "success" );
 
846
 
 
847
        if ((rc == LDAP_REFERRAL) && (referrals)) {
 
848
                send_referrals_from_entry(pb, referrals);
 
849
        } else {
 
850
                send_ldap_result( pb, rc, NULL, errMesg, 0, NULL );
 
851
        }
 
852
 
 
853
        slapi_ch_free_string(&oldPasswd);
 
854
        slapi_ch_free_string(&newPasswd);
 
855
        /* Either this is the same pointer that we allocated and set above,
 
856
         * or whoever used it should have freed it and allocated a new
 
857
         * value that we need to free here */
 
858
        slapi_pblock_get( pb, SLAPI_ORIGINAL_TARGET, &otdn );
 
859
        if (otdn != dn) {
 
860
                slapi_ch_free_string(&otdn);
 
861
        }
 
862
        slapi_pblock_get(pb, SLAPI_TARGET_SDN, &target_sdn);
 
863
        if (bindSDN != target_sdn) {
 
864
                slapi_sdn_free(&bindSDN);
 
865
        }
 
866
        /* slapi_pblock_get SLAPI_CONN_DN does strdup */
 
867
        slapi_ch_free_string(&bindDN);
 
868
        slapi_sdn_free(&target_sdn);
 
869
        slapi_pblock_set(pb, SLAPI_TARGET_SDN, NULL);
 
870
        slapi_pblock_set( pb, SLAPI_ORIGINAL_TARGET, NULL );
 
871
        slapi_ch_free_string(&authmethod);
 
872
        slapi_entry_free(referrals);
 
873
 
 
874
        if ( targetEntry != NULL ){
 
875
                slapi_entry_free (targetEntry); 
 
876
        }
 
877
        
 
878
        if ( ber != NULL ){
 
879
                ber_free(ber, 1);
 
880
                ber = NULL;
 
881
        }
 
882
        
 
883
        /* We can free the generated password bval now */
 
884
        ber_bvfree(gen_passwd);
 
885
 
 
886
        return( SLAPI_PLUGIN_EXTENDED_SENT_RESULT );
 
887
 
 
888
}/* passwd_modify_extop */
 
889
 
 
890
 
 
891
static char *passwd_oid_list[] = {
 
892
        EXTOP_PASSWD_OID,
 
893
        NULL
 
894
};
 
895
 
 
896
 
 
897
static char *passwd_name_list[] = {
 
898
        "passwd_modify_extop",
 
899
        NULL
 
900
};
 
901
 
 
902
 
 
903
/* Initialization function */
 
904
int passwd_modify_init( Slapi_PBlock *pb )
 
905
{
 
906
        char    **argv;
 
907
 
 
908
        /* Get the arguments appended to the plugin extendedop directive. The first argument 
 
909
         * (after the standard arguments for the directive) should contain the OID of the
 
910
         * extended operation.
 
911
         */ 
 
912
 
 
913
        if ( slapi_pblock_get( pb, SLAPI_PLUGIN_ARGV, &argv ) != 0 ) {
 
914
                slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_init", "Could not get argv\n" );
 
915
                return( -1 );
 
916
        }
 
917
 
 
918
        /* Compare the OID specified in the configuration file against the Passwd OID. */
 
919
 
 
920
        if ( argv == NULL || strcmp( argv[0], EXTOP_PASSWD_OID ) != 0 ) {
 
921
                slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_init", 
 
922
                                 "OID is missing or is not %s\n", EXTOP_PASSWD_OID );
 
923
                return( -1 );
 
924
        } else {
 
925
                slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_init", 
 
926
                                 "Registering plug-in for Password Modify extended op %s.\n", argv[0] /* oid */);
 
927
        }
 
928
 
 
929
        /* Register the plug-in function as an extended operation
 
930
         * plug-in function that handles the operation identified by
 
931
         * OID 1.3.6.1.4.1.4203.1.11.1 .  Also specify the version of the server 
 
932
         * plug-in */ 
 
933
        if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 || 
 
934
             slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&passwdopdesc ) != 0 ||
 
935
             slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_FN, (void *) passwd_modify_extop ) != 0 ||
 
936
             slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_OIDLIST, passwd_oid_list ) != 0 ||
 
937
             slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_NAMELIST, passwd_name_list ) != 0 ) {
 
938
 
 
939
                slapi_log_error( SLAPI_LOG_PLUGIN, "passwd_modify_init",
 
940
                                 "Failed to set plug-in version, function, and OID.\n" );
 
941
                return( -1 );
 
942
        }
 
943
        
 
944
        return( 0 );
 
945
}
 
946
 
 
947
int passwd_modify_register_plugin()
 
948
{
 
949
        slapi_register_plugin( "extendedop", 1 /* Enabled */, "passwd_modify_init", 
 
950
                        passwd_modify_init, "Password Modify extended operation",
 
951
                        passwd_oid_list, NULL );
 
952
 
 
953
        return 0;
 
954
}
 
955