1*549b59edSchristos /* $NetBSD: ppolicy.c,v 1.3 2021/08/14 16:14:56 christos Exp $ */
24e6df137Slukem
3d11b170bStron /* $OpenLDAP$ */
42de962bdSlukem /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
52de962bdSlukem *
6*549b59edSchristos * Copyright 2004-2021 The OpenLDAP Foundation.
72de962bdSlukem * Portions Copyright 2004 Hewlett-Packard Company.
82de962bdSlukem * Portions Copyright 2004 Howard Chu, Symas Corp.
92de962bdSlukem * All rights reserved.
102de962bdSlukem *
112de962bdSlukem * Redistribution and use in source and binary forms, with or without
122de962bdSlukem * modification, are permitted only as authorized by the OpenLDAP
132de962bdSlukem * Public License.
142de962bdSlukem *
152de962bdSlukem * A copy of this license is available in the file LICENSE in the
162de962bdSlukem * top-level directory of the distribution or, alternatively, at
172de962bdSlukem * <http://www.OpenLDAP.org/license.html>.
182de962bdSlukem */
192de962bdSlukem /* ACKNOWLEDGEMENTS:
202de962bdSlukem * This work was developed by Howard Chu for inclusion in
212de962bdSlukem * OpenLDAP Software, based on prior work by Neil Dunbar (HP).
222de962bdSlukem * This work was sponsored by the Hewlett-Packard Company.
232de962bdSlukem */
242de962bdSlukem
25376af7d7Schristos #include <sys/cdefs.h>
26*549b59edSchristos __RCSID("$NetBSD: ppolicy.c,v 1.3 2021/08/14 16:14:56 christos Exp $");
27376af7d7Schristos
282de962bdSlukem #include "portable.h"
292de962bdSlukem
302de962bdSlukem #include <stdio.h>
312de962bdSlukem #include <ac/stdlib.h>
322de962bdSlukem #include <ac/string.h>
332de962bdSlukem #include <ac/time.h>
342de962bdSlukem
352de962bdSlukem #include "ldap-int.h"
362de962bdSlukem
372de962bdSlukem #ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
382de962bdSlukem
392de962bdSlukem /* IMPLICIT TAGS, all context-specific */
402de962bdSlukem #define PPOLICY_WARNING 0xa0L /* constructed + 0 */
412de962bdSlukem #define PPOLICY_ERROR 0x81L /* primitive + 1 */
422de962bdSlukem
432de962bdSlukem #define PPOLICY_EXPIRE 0x80L /* primitive + 0 */
442de962bdSlukem #define PPOLICY_GRACE 0x81L /* primitive + 1 */
452de962bdSlukem
462de962bdSlukem /*---
472de962bdSlukem ldap_create_passwordpolicy_control
482de962bdSlukem
492de962bdSlukem Create and encode the Password Policy Request
502de962bdSlukem
512de962bdSlukem ld (IN) An LDAP session handle, as obtained from a call to
522de962bdSlukem ldap_init().
532de962bdSlukem
542de962bdSlukem ctrlp (OUT) A result parameter that will be assigned the address
552de962bdSlukem of an LDAPControl structure that contains the
562de962bdSlukem passwordPolicyRequest control created by this function.
572de962bdSlukem The memory occupied by the LDAPControl structure
582de962bdSlukem SHOULD be freed when it is no longer in use by
592de962bdSlukem calling ldap_control_free().
602de962bdSlukem
612de962bdSlukem
622de962bdSlukem There is no control value for a password policy request
632de962bdSlukem ---*/
642de962bdSlukem
652de962bdSlukem int
ldap_create_passwordpolicy_control(LDAP * ld,LDAPControl ** ctrlp)662de962bdSlukem ldap_create_passwordpolicy_control( LDAP *ld,
672de962bdSlukem LDAPControl **ctrlp )
682de962bdSlukem {
692de962bdSlukem assert( ld != NULL );
702de962bdSlukem assert( LDAP_VALID( ld ) );
712de962bdSlukem assert( ctrlp != NULL );
722de962bdSlukem
732de962bdSlukem ld->ld_errno = ldap_control_create( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
742de962bdSlukem 0, NULL, 0, ctrlp );
752de962bdSlukem
762de962bdSlukem return ld->ld_errno;
772de962bdSlukem }
782de962bdSlukem
792de962bdSlukem
802de962bdSlukem /*---
812de962bdSlukem ldap_parse_passwordpolicy_control
822de962bdSlukem
832de962bdSlukem Decode the passwordPolicyResponse control and return information.
842de962bdSlukem
852de962bdSlukem ld (IN) An LDAP session handle.
862de962bdSlukem
872de962bdSlukem ctrl (IN) The address of an
882de962bdSlukem LDAPControl structure, either obtained
89*549b59edSchristos by running through the list of response controls or
902de962bdSlukem by a call to ldap_control_find().
912de962bdSlukem
922de962bdSlukem exptimep (OUT) This result parameter is filled in with the number of seconds before
932de962bdSlukem the password will expire, if expiration is imminent
942de962bdSlukem (imminency defined by the password policy). If expiration
952de962bdSlukem is not imminent, the value is set to -1.
962de962bdSlukem
972de962bdSlukem gracep (OUT) This result parameter is filled in with the number of grace logins after
982de962bdSlukem the password has expired, before no further login attempts
992de962bdSlukem will be allowed.
1002de962bdSlukem
1012de962bdSlukem errorcodep (OUT) This result parameter is filled in with the error code of the password operation
1022de962bdSlukem If no error was detected, this error is set to PP_noError.
1032de962bdSlukem
1042de962bdSlukem Ber encoding
1052de962bdSlukem
1062de962bdSlukem PasswordPolicyResponseValue ::= SEQUENCE {
1072de962bdSlukem warning [0] CHOICE {
1082de962bdSlukem timeBeforeExpiration [0] INTEGER (0 .. maxInt),
1092de962bdSlukem graceLoginsRemaining [1] INTEGER (0 .. maxInt) } OPTIONAL
1102de962bdSlukem error [1] ENUMERATED {
1112de962bdSlukem passwordExpired (0),
1122de962bdSlukem accountLocked (1),
1132de962bdSlukem changeAfterReset (2),
1142de962bdSlukem passwordModNotAllowed (3),
1152de962bdSlukem mustSupplyOldPassword (4),
1162de962bdSlukem invalidPasswordSyntax (5),
1172de962bdSlukem passwordTooShort (6),
1182de962bdSlukem passwordTooYoung (7),
1192de962bdSlukem passwordInHistory (8) } OPTIONAL }
1202de962bdSlukem
1212de962bdSlukem ---*/
1222de962bdSlukem
1232de962bdSlukem int
ldap_parse_passwordpolicy_control(LDAP * ld,LDAPControl * ctrl,ber_int_t * expirep,ber_int_t * gracep,LDAPPasswordPolicyError * errorp)1242de962bdSlukem ldap_parse_passwordpolicy_control(
1252de962bdSlukem LDAP *ld,
1262de962bdSlukem LDAPControl *ctrl,
1272de962bdSlukem ber_int_t *expirep,
1282de962bdSlukem ber_int_t *gracep,
1292de962bdSlukem LDAPPasswordPolicyError *errorp )
1302de962bdSlukem {
1312de962bdSlukem BerElement *ber;
1322de962bdSlukem int exp = -1, grace = -1;
1332de962bdSlukem ber_tag_t tag;
1342de962bdSlukem ber_len_t berLen;
1352de962bdSlukem char *last;
1362de962bdSlukem int err = PP_noError;
1372de962bdSlukem
1382de962bdSlukem assert( ld != NULL );
1392de962bdSlukem assert( LDAP_VALID( ld ) );
1402de962bdSlukem assert( ctrl != NULL );
1412de962bdSlukem
142d11b170bStron if ( !ctrl->ldctl_value.bv_val ) {
143d11b170bStron ld->ld_errno = LDAP_DECODING_ERROR;
144d11b170bStron return(ld->ld_errno);
145d11b170bStron }
146d11b170bStron
1472de962bdSlukem /* Create a BerElement from the berval returned in the control. */
1482de962bdSlukem ber = ber_init(&ctrl->ldctl_value);
1492de962bdSlukem
1502de962bdSlukem if (ber == NULL) {
1512de962bdSlukem ld->ld_errno = LDAP_NO_MEMORY;
1522de962bdSlukem return(ld->ld_errno);
1532de962bdSlukem }
1542de962bdSlukem
1552de962bdSlukem tag = ber_peek_tag( ber, &berLen );
1562de962bdSlukem if (tag != LBER_SEQUENCE) goto exit;
1572de962bdSlukem
1582de962bdSlukem for( tag = ber_first_element( ber, &berLen, &last );
1592de962bdSlukem tag != LBER_DEFAULT;
1602de962bdSlukem tag = ber_next_element( ber, &berLen, last ) )
1612de962bdSlukem {
1622de962bdSlukem switch (tag) {
1632de962bdSlukem case PPOLICY_WARNING:
1642de962bdSlukem ber_skip_tag(ber, &berLen );
1652de962bdSlukem tag = ber_peek_tag( ber, &berLen );
1662de962bdSlukem switch( tag ) {
1672de962bdSlukem case PPOLICY_EXPIRE:
1682de962bdSlukem if (ber_get_int( ber, &exp ) == LBER_DEFAULT) goto exit;
1692de962bdSlukem break;
1702de962bdSlukem case PPOLICY_GRACE:
1712de962bdSlukem if (ber_get_int( ber, &grace ) == LBER_DEFAULT) goto exit;
1722de962bdSlukem break;
1732de962bdSlukem default:
1742de962bdSlukem goto exit;
1752de962bdSlukem }
1762de962bdSlukem break;
1772de962bdSlukem case PPOLICY_ERROR:
1782de962bdSlukem if (ber_get_enum( ber, &err ) == LBER_DEFAULT) goto exit;
1792de962bdSlukem break;
1802de962bdSlukem default:
1812de962bdSlukem goto exit;
1822de962bdSlukem }
1832de962bdSlukem }
1842de962bdSlukem
1852de962bdSlukem ber_free(ber, 1);
1862de962bdSlukem
1872de962bdSlukem /* Return data to the caller for items that were requested. */
1882de962bdSlukem if (expirep) *expirep = exp;
1892de962bdSlukem if (gracep) *gracep = grace;
1902de962bdSlukem if (errorp) *errorp = err;
1912de962bdSlukem
1922de962bdSlukem ld->ld_errno = LDAP_SUCCESS;
1932de962bdSlukem return(ld->ld_errno);
1942de962bdSlukem
1952de962bdSlukem exit:
1962de962bdSlukem ber_free(ber, 1);
1972de962bdSlukem ld->ld_errno = LDAP_DECODING_ERROR;
1982de962bdSlukem return(ld->ld_errno);
1992de962bdSlukem }
2002de962bdSlukem
2012de962bdSlukem const char *
ldap_passwordpolicy_err2txt(LDAPPasswordPolicyError err)2022de962bdSlukem ldap_passwordpolicy_err2txt( LDAPPasswordPolicyError err )
2032de962bdSlukem {
2042de962bdSlukem switch(err) {
2052de962bdSlukem case PP_passwordExpired: return "Password expired";
2062de962bdSlukem case PP_accountLocked: return "Account locked";
2072de962bdSlukem case PP_changeAfterReset: return "Password must be changed";
2082de962bdSlukem case PP_passwordModNotAllowed: return "Policy prevents password modification";
2092de962bdSlukem case PP_mustSupplyOldPassword: return "Policy requires old password in order to change password";
2102de962bdSlukem case PP_insufficientPasswordQuality: return "Password fails quality checks";
2112de962bdSlukem case PP_passwordTooShort: return "Password is too short for policy";
2122de962bdSlukem case PP_passwordTooYoung: return "Password has been changed too recently";
2132de962bdSlukem case PP_passwordInHistory: return "New password is in list of old passwords";
214*549b59edSchristos case PP_passwordTooLong: return "Password is too long for policy";
2152de962bdSlukem case PP_noError: return "No error";
2162de962bdSlukem default: return "Unknown error code";
2172de962bdSlukem }
2182de962bdSlukem }
2192de962bdSlukem
2202de962bdSlukem #endif /* LDAP_CONTROL_PASSWORDPOLICYREQUEST */
221*549b59edSchristos
222*549b59edSchristos #ifdef LDAP_CONTROL_X_PASSWORD_EXPIRING
223*549b59edSchristos
224*549b59edSchristos int
ldap_parse_password_expiring_control(LDAP * ld,LDAPControl * ctrl,long * secondsp)225*549b59edSchristos ldap_parse_password_expiring_control(
226*549b59edSchristos LDAP *ld,
227*549b59edSchristos LDAPControl *ctrl,
228*549b59edSchristos long *secondsp )
229*549b59edSchristos {
230*549b59edSchristos long seconds = 0;
231*549b59edSchristos char buf[sizeof("-2147483648")];
232*549b59edSchristos char *next;
233*549b59edSchristos
234*549b59edSchristos assert( ld != NULL );
235*549b59edSchristos assert( LDAP_VALID( ld ) );
236*549b59edSchristos assert( ctrl != NULL );
237*549b59edSchristos
238*549b59edSchristos if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ||
239*549b59edSchristos ctrl->ldctl_value.bv_len >= sizeof(buf) ) {
240*549b59edSchristos ld->ld_errno = LDAP_DECODING_ERROR;
241*549b59edSchristos return(ld->ld_errno);
242*549b59edSchristos }
243*549b59edSchristos
244*549b59edSchristos memcpy( buf, ctrl->ldctl_value.bv_val, ctrl->ldctl_value.bv_len );
245*549b59edSchristos buf[ctrl->ldctl_value.bv_len] = '\0';
246*549b59edSchristos
247*549b59edSchristos seconds = strtol( buf, &next, 10 );
248*549b59edSchristos if ( next == buf || next[0] != '\0' ) goto exit;
249*549b59edSchristos
250*549b59edSchristos if ( secondsp != NULL ) {
251*549b59edSchristos *secondsp = seconds;
252*549b59edSchristos }
253*549b59edSchristos
254*549b59edSchristos ld->ld_errno = LDAP_SUCCESS;
255*549b59edSchristos return(ld->ld_errno);
256*549b59edSchristos
257*549b59edSchristos exit:
258*549b59edSchristos ld->ld_errno = LDAP_DECODING_ERROR;
259*549b59edSchristos return(ld->ld_errno);
260*549b59edSchristos }
261*549b59edSchristos
262*549b59edSchristos #endif /* LDAP_CONTROL_X_PASSWORD_EXPIRING */
263