diff --git a/include/ldap.h b/include/ldap.h
index 62f8cbcee6..a009ec144c 100644
--- a/include/ldap.h
+++ b/include/ldap.h
@@ -380,6 +380,18 @@ typedef struct ldapcontrol {
#define LDAP_CONTROL_VLVREQUEST "2.16.840.1.113730.3.4.9"
#define LDAP_CONTROL_VLVRESPONSE "2.16.840.1.113730.3.4.10"
+/* Sun's analogue to ppolicy */
+#define LDAP_CONTROL_X_ACCOUNT_USABILITY "1.3.6.1.4.1.42.2.27.9.5.8"
+
+#define LDAP_TAG_X_ACCOUNT_USABILITY_AVAILABLE ((ber_tag_t) 0x80U) /* primitive + 0 */
+#define LDAP_TAG_X_ACCOUNT_USABILITY_NOT_AVAILABLE ((ber_tag_t) 0xA1U) /* constructed + 1 */
+
+#define LDAP_TAG_X_ACCOUNT_USABILITY_INACTIVE ((ber_tag_t) 0x80U) /* primitive + 0 */
+#define LDAP_TAG_X_ACCOUNT_USABILITY_RESET ((ber_tag_t) 0x81U) /* primitive + 1 */
+#define LDAP_TAG_X_ACCOUNT_USABILITY_EXPIRED ((ber_tag_t) 0x82U) /* primitive + 2 */
+#define LDAP_TAG_X_ACCOUNT_USABILITY_REMAINING_GRACE ((ber_tag_t) 0x83U) /* primitive + 3 */
+#define LDAP_TAG_X_ACCOUNT_USABILITY_UNTIL_UNLOCK ((ber_tag_t) 0x84U) /* primitive + 4 */
+
/* LDAP Unsolicited Notifications */
#define LDAP_NOTICE_OF_DISCONNECTION "1.3.6.1.4.1.1466.20036" /* RFC 4511 */
#define LDAP_NOTICE_DISCONNECT LDAP_NOTICE_OF_DISCONNECTION
@@ -2685,6 +2697,33 @@ ldap_parse_entrychange_control LDAP_P((
int *chgnumpresentp,
long *chgnump ));
+/* in account_usability.c */
+
+LDAP_F( int )
+ldap_create_accountusability_control LDAP_P((
+ LDAP *ld,
+ LDAPControl **ctrlp ));
+
+typedef struct LDAPAccountUsabilityMoreInfo {
+ ber_int_t inactive;
+ ber_int_t reset;
+ ber_int_t expired;
+ ber_int_t remaining_grace;
+ ber_int_t seconds_before_unlock;
+} LDAPAccountUsabilityMoreInfo;
+
+typedef union LDAPAccountUsability {
+ ber_int_t seconds_remaining;
+ LDAPAccountUsabilityMoreInfo more_info;
+} LDAPAccountUsability;
+
+LDAP_F( int )
+ldap_parse_accountusability_control LDAP_P((
+ LDAP *ld,
+ LDAPControl *ctrl,
+ int *availablep,
+ LDAPAccountUsability *usabilityp ));
+
/*
* high level LDIF to LDAP structure support
diff --git a/libraries/libldap/Makefile.in b/libraries/libldap/Makefile.in
index 5ae45de007..1bb7518093 100644
--- a/libraries/libldap/Makefile.in
+++ b/libraries/libldap/Makefile.in
@@ -30,7 +30,8 @@ SRCS = bind.c open.c result.c error.c compare.c search.c \
turn.c ppolicy.c dds.c txn.c ldap_sync.c stctrl.c \
assertion.c deref.c ldifutil.c ldif.c fetch.c lbase64.c \
msctrl.c psearchctrl.c threads.c rdwr.c tpool.c rq.c \
- thr_posix.c thr_thr.c thr_nt.c thr_pth.c thr_debug.c
+ thr_posix.c thr_thr.c thr_nt.c thr_pth.c thr_debug.c \
+ account_usability.c
OBJS = bind.lo open.lo result.lo error.lo compare.lo search.lo \
controls.lo messages.lo references.lo extended.lo cyrus.lo \
@@ -45,7 +46,8 @@ OBJS = bind.lo open.lo result.lo error.lo compare.lo search.lo \
turn.lo ppolicy.lo dds.lo txn.lo ldap_sync.lo stctrl.lo \
assertion.lo deref.lo ldifutil.lo ldif.lo fetch.lo lbase64.lo \
msctrl.lo psearchctrl.lo threads.lo rdwr.lo tpool.lo rq.lo \
- thr_posix.lo thr_thr.lo thr_nt.lo thr_pth.lo thr_debug.lo
+ thr_posix.lo thr_thr.lo thr_nt.lo thr_pth.lo thr_debug.lo \
+ account_usability.lo
LDAP_INCDIR= ../../include
LDAP_LIBDIR= ../../libraries
diff --git a/libraries/libldap/account_usability.c b/libraries/libldap/account_usability.c
new file mode 100644
index 0000000000..77f3fb03e1
--- /dev/null
+++ b/libraries/libldap/account_usability.c
@@ -0,0 +1,128 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software .
+ *
+ * Copyright 2004-2020 The OpenLDAP Foundation.
+ * Portions Copyright 2004 Hewlett-Packard Company.
+ * Portions Copyright 2004 Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * .
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Howard Chu for inclusion in
+ * OpenLDAP Software, based on prior work by Neil Dunbar (HP).
+ * This work was sponsored by the Hewlett-Packard Company.
+ */
+
+#include "portable.h"
+
+#include "ldap-int.h"
+
+#ifdef LDAP_CONTROL_X_ACCOUNT_USABILITY
+
+int
+ldap_create_accountusability_control( LDAP *ld,
+ LDAPControl **ctrlp )
+{
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( ctrlp != NULL );
+
+ ld->ld_errno = ldap_control_create( LDAP_CONTROL_X_ACCOUNT_USABILITY,
+ 0, NULL, 0, ctrlp );
+
+ return ld->ld_errno;
+}
+
+int
+ldap_parse_accountusability_control(
+ LDAP *ld,
+ LDAPControl *ctrl,
+ int *availablep,
+ LDAPAccountUsability *usabilityp )
+{
+ BerElement *ber;
+ int available = 0;
+ ber_tag_t tag;
+ ber_len_t berLen;
+ char *last;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( ctrl != NULL );
+
+ if ( !ctrl->ldctl_value.bv_val ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+ }
+
+ /* Create a BerElement from the berval returned in the control. */
+ ber = ber_init(&ctrl->ldctl_value);
+
+ if (ber == NULL) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return(ld->ld_errno);
+ }
+
+ tag = ber_peek_tag( ber, &berLen );
+
+ if ( tag == LDAP_TAG_X_ACCOUNT_USABILITY_AVAILABLE ) {
+ available = 1;
+
+ if ( usabilityp != NULL ) {
+ if (ber_get_int( ber, &usabilityp->seconds_remaining ) == LBER_DEFAULT) goto exit;
+ }
+ } else if ( tag == LDAP_TAG_X_ACCOUNT_USABILITY_NOT_AVAILABLE ) {
+ available = 0;
+ LDAPAccountUsabilityMoreInfo more_info = { 0, 0, 0, -1, -1 };
+
+ ber_skip_tag( ber, &berLen );
+ while ( (tag = ber_peek_tag( ber, &berLen )) != LBER_DEFAULT ) {
+ switch (tag) {
+ case LDAP_TAG_X_ACCOUNT_USABILITY_INACTIVE:
+ if (ber_get_boolean( ber, &more_info.inactive ) == LBER_DEFAULT) goto exit;
+ break;
+ case LDAP_TAG_X_ACCOUNT_USABILITY_RESET:
+ if (ber_get_boolean( ber, &more_info.reset ) == LBER_DEFAULT) goto exit;
+ break;
+ case LDAP_TAG_X_ACCOUNT_USABILITY_EXPIRED:
+ if (ber_get_boolean( ber, &more_info.expired ) == LBER_DEFAULT) goto exit;
+ break;
+ case LDAP_TAG_X_ACCOUNT_USABILITY_REMAINING_GRACE:
+ if (ber_get_int( ber, &more_info.remaining_grace ) == LBER_DEFAULT) goto exit;
+ break;
+ case LDAP_TAG_X_ACCOUNT_USABILITY_UNTIL_UNLOCK:
+ if (ber_get_int( ber, &more_info.seconds_before_unlock ) == LBER_DEFAULT) goto exit;
+ break;
+ default:
+ goto exit;
+ }
+ }
+ if ( usabilityp != NULL ) {
+ usabilityp->more_info = more_info;
+ }
+ } else {
+ goto exit;
+ }
+ if ( availablep != NULL ) {
+ *availablep = available;
+ }
+
+ ber_free(ber, 1);
+
+ ld->ld_errno = LDAP_SUCCESS;
+ return(ld->ld_errno);
+
+ exit:
+ ber_free(ber, 1);
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+}
+
+#endif /* LDAP_CONTROL_X_ACCOUNT_USABILITY */