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
65int
66ldap_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
123int
124ldap_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
201const char *
202ldap_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
224int
225ldap_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