1 /*        $NetBSD: ppolicy.c,v 1.3 2021/08/14 16:14:56 christos Exp $ */
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 2004-2021 The OpenLDAP Foundation.
7  * Portions Copyright 2004 Hewlett-Packard Company.
8  * Portions Copyright 2004 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 work was developed by Howard Chu for inclusion in
21  * OpenLDAP Software, based on prior work by Neil Dunbar (HP).
22  * This work was sponsored by the Hewlett-Packard Company.
23  */
24 
25 #include <sys/cdefs.h>
26 __RCSID("$NetBSD: ppolicy.c,v 1.3 2021/08/14 16:14:56 christos Exp $");
27 
28 #include "portable.h"
29 
30 #include <stdio.h>
31 #include <ac/stdlib.h>
32 #include <ac/string.h>
33 #include <ac/time.h>
34 
35 #include "ldap-int.h"
36 
37 #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
38 
39 /* IMPLICIT TAGS, all context-specific */
40 #define PPOLICY_WARNING 0xa0L /* constructed + 0 */
41 #define PPOLICY_ERROR 0x81L             /* primitive + 1 */
42 
43 #define PPOLICY_EXPIRE 0x80L  /* primitive + 0 */
44 #define PPOLICY_GRACE  0x81L  /* primitive + 1 */
45 
46 /*---
47    ldap_create_passwordpolicy_control
48 
49    Create and encode the Password Policy Request
50 
51    ld        (IN)  An LDAP session handle, as obtained from a call to
52                                            ldap_init().
53 
54    ctrlp     (OUT) A result parameter that will be assigned the address
55                                            of an LDAPControl structure that contains the
56                                            passwordPolicyRequest control created by this function.
57                                            The memory occupied by the LDAPControl structure
58                                            SHOULD be freed when it is no longer in use by
59                                            calling ldap_control_free().
60 
61 
62    There is no control value for a password policy request
63  ---*/
64 
65 int
ldap_create_passwordpolicy_control(LDAP * ld,LDAPControl ** ctrlp)66 ldap_create_passwordpolicy_control( LDAP *ld,
67                                     LDAPControl **ctrlp )
68 {
69           assert( ld != NULL );
70           assert( LDAP_VALID( ld ) );
71           assert( ctrlp != NULL );
72 
73           ld->ld_errno = ldap_control_create( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
74                     0, NULL, 0, ctrlp );
75 
76           return ld->ld_errno;
77 }
78 
79 
80 /*---
81    ldap_parse_passwordpolicy_control
82 
83    Decode the passwordPolicyResponse control and return information.
84 
85    ld           (IN)   An LDAP session handle.
86 
87    ctrl         (IN)   The address of an
88                                                      LDAPControl structure, either obtained
89                                                      by running through the list of response controls or
90                                                      by a call to ldap_control_find().
91 
92    exptimep     (OUT)  This result parameter is filled in with the number of seconds before
93                                            the password will expire, if expiration is imminent
94                                            (imminency defined by the password policy). If expiration
95                                            is not imminent, the value is set to -1.
96 
97    gracep       (OUT)  This result parameter is filled in with the number of grace logins after
98                                            the password has expired, before no further login attempts
99                                            will be allowed.
100 
101    errorcodep   (OUT)  This result parameter is filled in with the error code of the password operation
102                                            If no error was detected, this error is set to PP_noError.
103 
104    Ber encoding
105 
106    PasswordPolicyResponseValue ::= SEQUENCE {
107        warning [0] CHOICE {
108            timeBeforeExpiration [0] INTEGER (0 .. maxInt),
109            graceLoginsRemaining [1] INTEGER (0 .. maxInt) } OPTIONAL
110        error [1] ENUMERATED {
111            passwordExpired        (0),
112            accountLocked          (1),
113            changeAfterReset       (2),
114            passwordModNotAllowed  (3),
115            mustSupplyOldPassword  (4),
116            invalidPasswordSyntax  (5),
117            passwordTooShort       (6),
118            passwordTooYoung       (7),
119            passwordInHistory      (8) } OPTIONAL }
120 
121 ---*/
122 
123 int
ldap_parse_passwordpolicy_control(LDAP * ld,LDAPControl * ctrl,ber_int_t * expirep,ber_int_t * gracep,LDAPPasswordPolicyError * errorp)124 ldap_parse_passwordpolicy_control(
125           LDAP           *ld,
126           LDAPControl    *ctrl,
127           ber_int_t      *expirep,
128           ber_int_t      *gracep,
129           LDAPPasswordPolicyError *errorp )
130 {
131           BerElement  *ber;
132           int exp = -1, grace = -1;
133           ber_tag_t tag;
134           ber_len_t berLen;
135         char *last;
136           int err = PP_noError;
137 
138           assert( ld != NULL );
139           assert( LDAP_VALID( ld ) );
140           assert( ctrl != NULL );
141 
142           if ( !ctrl->ldctl_value.bv_val ) {
143                     ld->ld_errno = LDAP_DECODING_ERROR;
144                     return(ld->ld_errno);
145           }
146 
147           /* Create a BerElement from the berval returned in the control. */
148           ber = ber_init(&ctrl->ldctl_value);
149 
150           if (ber == NULL) {
151                     ld->ld_errno = LDAP_NO_MEMORY;
152                     return(ld->ld_errno);
153           }
154 
155           tag = ber_peek_tag( ber, &berLen );
156           if (tag != LBER_SEQUENCE) goto exit;
157 
158           for( tag = ber_first_element( ber, &berLen, &last );
159                     tag != LBER_DEFAULT;
160                     tag = ber_next_element( ber, &berLen, last ) )
161           {
162                     switch (tag) {
163                     case PPOLICY_WARNING:
164                               ber_skip_tag(ber, &berLen );
165                               tag = ber_peek_tag( ber, &berLen );
166                               switch( tag ) {
167                               case PPOLICY_EXPIRE:
168                                         if (ber_get_int( ber, &exp ) == LBER_DEFAULT) goto exit;
169                                         break;
170                               case PPOLICY_GRACE:
171                                         if (ber_get_int( ber, &grace ) == LBER_DEFAULT) goto exit;
172                                         break;
173                               default:
174                                         goto exit;
175                               }
176                               break;
177                     case PPOLICY_ERROR:
178                               if (ber_get_enum( ber, &err ) == LBER_DEFAULT) goto exit;
179                               break;
180                     default:
181                               goto exit;
182                     }
183           }
184 
185           ber_free(ber, 1);
186 
187           /* Return data to the caller for items that were requested. */
188           if (expirep) *expirep = exp;
189           if (gracep) *gracep = grace;
190           if (errorp) *errorp = err;
191 
192           ld->ld_errno = LDAP_SUCCESS;
193           return(ld->ld_errno);
194 
195   exit:
196           ber_free(ber, 1);
197           ld->ld_errno = LDAP_DECODING_ERROR;
198           return(ld->ld_errno);
199 }
200 
201 const char *
ldap_passwordpolicy_err2txt(LDAPPasswordPolicyError err)202 ldap_passwordpolicy_err2txt( LDAPPasswordPolicyError err )
203 {
204           switch(err) {
205           case PP_passwordExpired: return "Password expired";
206           case PP_accountLocked: return "Account locked";
207           case PP_changeAfterReset: return "Password must be changed";
208           case PP_passwordModNotAllowed: return "Policy prevents password modification";
209           case PP_mustSupplyOldPassword: return "Policy requires old password in order to change password";
210           case PP_insufficientPasswordQuality: return "Password fails quality checks";
211           case PP_passwordTooShort: return "Password is too short for policy";
212           case PP_passwordTooYoung: return "Password has been changed too recently";
213           case PP_passwordInHistory: return "New password is in list of old passwords";
214           case PP_passwordTooLong: return "Password is too long for policy";
215           case PP_noError: return "No error";
216           default: return "Unknown error code";
217           }
218 }
219 
220 #endif /* LDAP_CONTROL_PASSWORDPOLICYREQUEST */
221 
222 #ifdef LDAP_CONTROL_X_PASSWORD_EXPIRING
223 
224 int
ldap_parse_password_expiring_control(LDAP * ld,LDAPControl * ctrl,long * secondsp)225 ldap_parse_password_expiring_control(
226           LDAP           *ld,
227           LDAPControl    *ctrl,
228           long           *secondsp )
229 {
230           long seconds = 0;
231           char buf[sizeof("-2147483648")];
232           char *next;
233 
234           assert( ld != NULL );
235           assert( LDAP_VALID( ld ) );
236           assert( ctrl != NULL );
237 
238           if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ||
239                     ctrl->ldctl_value.bv_len >= sizeof(buf) ) {
240                     ld->ld_errno = LDAP_DECODING_ERROR;
241                     return(ld->ld_errno);
242           }
243 
244           memcpy( buf, ctrl->ldctl_value.bv_val, ctrl->ldctl_value.bv_len );
245           buf[ctrl->ldctl_value.bv_len] = '\0';
246 
247           seconds = strtol( buf, &next, 10 );
248           if ( next == buf || next[0] != '\0' ) goto exit;
249 
250           if ( secondsp != NULL ) {
251                     *secondsp = seconds;
252           }
253 
254           ld->ld_errno = LDAP_SUCCESS;
255           return(ld->ld_errno);
256 
257   exit:
258           ld->ld_errno = LDAP_DECODING_ERROR;
259           return(ld->ld_errno);
260 }
261 
262 #endif /* LDAP_CONTROL_X_PASSWORD_EXPIRING */
263