summaryrefslogtreecommitdiffstats
path: root/contrib/slapd-modules/nssov/nss-pam-ldapd
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--contrib/slapd-modules/nssov/nss-pam-ldapd/README15
-rw-r--r--contrib/slapd-modules/nssov/nss-pam-ldapd/attrs.h91
-rw-r--r--contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd-prot.h391
-rw-r--r--contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd.h305
-rw-r--r--contrib/slapd-modules/nssov/nss-pam-ldapd/tio.c520
-rw-r--r--contrib/slapd-modules/nssov/nss-pam-ldapd/tio.h83
6 files changed, 1405 insertions, 0 deletions
diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/README b/contrib/slapd-modules/nssov/nss-pam-ldapd/README
new file mode 100644
index 0000000..4176ad7
--- /dev/null
+++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/README
@@ -0,0 +1,15 @@
+These files were pulled from the nss-pam-ldapd project version 0.9.4.
+Copyright notices are in the individual files.
+
+This is not the full distribution of nss-pam-ldapd, and does not
+include the client-side stub libraries. Get the latest release of
+nss-pam-ldapd from http://arthurdejong.org/nss-pam-ldapd/ to use
+this overlay.
+
+If your system already has the nss-pam-ldapd stub libraries
+installed, make sure the versions match the version number
+shown above. Otherwise, there may be incompatible differences in
+the protocols being used. Currently nssov requires at least
+version 0.9.0. If your system's version is older, you will need
+to install the client-side stubs from source.
+
diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/attrs.h b/contrib/slapd-modules/nssov/nss-pam-ldapd/attrs.h
new file mode 100644
index 0000000..2efedc6
--- /dev/null
+++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/attrs.h
@@ -0,0 +1,91 @@
+/*
+ attrs.h - wrapper macros for the gcc __attribute__(()) directive
+
+ Copyright (C) 2007, 2008, 2012 Arthur de Jong
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA
+*/
+
+#ifndef COMPAT__ATTRS_H
+#define COMPAT__ATTRS_H 1
+
+/* macro for testing the version of GCC */
+#define GCC_VERSION(major, minor) \
+ ((__GNUC__ > (major)) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor)))
+
+/* These are macros to use some gcc-specific flags in case they're available
+ and otherwise define them to empty strings. This allows us to give
+ the compiler some extra information.
+ See http://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html
+ for a list of attributes supported by gcc */
+
+/* this is used to flag function parameters that are not used in the function
+ body. */
+#if GCC_VERSION(3, 0)
+#define UNUSED(x) x __attribute__((__unused__))
+#else
+#define UNUSED(x) x
+#endif
+
+/* this is used to add extra format checking to the function calls as if this
+ was a printf()-like function */
+#if GCC_VERSION(3, 0)
+#define LIKE_PRINTF(format_idx, arg_idx) \
+ __attribute__((__format__(__printf__, format_idx, arg_idx)))
+#else
+#define LIKE_PRINTF(format_idx, arg_idx) /* no attribute */
+#endif
+
+/* indicates that the function is "pure": its result is purely based on
+ the parameters and has no side effects or used static data */
+#if GCC_VERSION(3, 0)
+#define PURE __attribute__((__pure__))
+#else
+#define PURE /* no attribute */
+#endif
+
+/* the function returns a new data structure that has been freshly
+ allocated */
+#if GCC_VERSION(3, 0)
+#define LIKE_MALLOC __attribute__((__malloc__))
+#else
+#define LIKE_MALLOC /* no attribute */
+#endif
+
+/* the function's return value should be used by the caller */
+#if GCC_VERSION(3, 4)
+#define MUST_USE __attribute__((__warn_unused_result__))
+#else
+#define MUST_USE /* no attribute */
+#endif
+
+/* the function's return value should be used by the caller */
+#if GCC_VERSION(2, 5)
+#define NORETURN __attribute__((__noreturn__))
+#else
+#define NORETURN /* no attribute */
+#endif
+
+/* define __STRING if it's not yet defined */
+#ifndef __STRING
+#ifdef __STDC__
+#define __STRING(x) #x
+#else /* __STDC__ */
+#define __STRING(x) "x"
+#endif /* not __STDC__ */
+#endif /* not __STRING */
+
+#endif /* not COMPAT__ATTRS_H */
diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd-prot.h b/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd-prot.h
new file mode 100644
index 0000000..21ec7c2
--- /dev/null
+++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd-prot.h
@@ -0,0 +1,391 @@
+/*
+ nslcd-prot.h - helper macros for reading and writing in protocol streams
+
+ Copyright (C) 2006 West Consulting
+ Copyright (C) 2006-2014 Arthur de Jong
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA
+*/
+
+#ifndef COMMON__NSLCD_PROT_H
+#define COMMON__NSLCD_PROT_H 1
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include "tio.h"
+
+/* If you use these macros you should define the following macros to
+ handle error conditions (these marcos should clean up and return from the
+ function):
+ ERROR_OUT_WRITEERROR(fp)
+ ERROR_OUT_READERROR(fp)
+ ERROR_OUT_BUFERROR(fp)
+ ERROR_OUT_NOSUCCESS(fp) */
+
+
+/* Debugging marcos that can be used to enable detailed protocol logging,
+ pass -DDEBUG_PROT to do overall protocol debugging, and -DDEBUG_PROT_DUMP
+ to dump the actual bytestream. */
+
+#ifdef DEBUG_PROT
+/* define a debugging macro to output logging */
+#include <string.h>
+#include <errno.h>
+#define DEBUG_PRINT(fmt, arg) \
+ fprintf(stderr, "%s:%d:%s: " fmt "\n", __FILE__, __LINE__, \
+ __PRETTY_FUNCTION__, arg);
+#else /* DEBUG_PROT */
+/* define an empty debug macro to disable logging */
+#define DEBUG_PRINT(fmt, arg)
+#endif /* not DEBUG_PROT */
+
+#ifdef DEBUG_PROT_DUMP
+/* define a debugging macro to output detailed logging */
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif /* HAVE_STDINT_H */
+static void debug_dump(const void *ptr, size_t size)
+{
+ int i;
+ for (i = 0; i < size; i++)
+ fprintf(stderr, " %02x", ((const uint8_t *)ptr)[i]);
+ fprintf(stderr, "\n");
+}
+#define DEBUG_DUMP(ptr, size) \
+ fprintf(stderr, "%s:%d:%s:", __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ debug_dump(ptr, size);
+#else /* DEBUG_PROT_DUMP */
+/* define an empty debug macro to disable logging */
+#define DEBUG_DUMP(ptr, size)
+#endif /* not DEBUG_PROT_DUMP */
+
+
+/* WRITE marcos, used for writing data, on write error they will
+ call the ERROR_OUT_WRITEERROR macro
+ these macros may require the availability of the following
+ variables:
+ int32_t tmpint32; - temporary variable
+ */
+
+#define WRITE(fp, ptr, size) \
+ DEBUG_PRINT("WRITE : var="__STRING(ptr)" size=%d", (int)size); \
+ DEBUG_DUMP(ptr, size); \
+ if (tio_write(fp, ptr, (size_t)size)) \
+ { \
+ char ebuf[128]; \
+ int saved_errno = errno; \
+ DEBUG_PRINT("WRITE : var="__STRING(ptr)" error: %s", \
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf))); \
+ ERROR_OUT_WRITEERROR(fp); \
+ }
+
+#define WRITE_INT32(fp, i) \
+ DEBUG_PRINT("WRITE_INT32 : var="__STRING(i)" int32=%08x", (int)i); \
+ tmpint32 = htonl((int32_t)(i)); \
+ WRITE(fp, &tmpint32, sizeof(int32_t))
+
+#define WRITE_STRING(fp, str) \
+ DEBUG_PRINT("WRITE_STRING: var="__STRING(str)" string=\"%s\"", (str)); \
+ if ((str) == NULL) \
+ { \
+ WRITE_INT32(fp, 0); \
+ } \
+ else \
+ { \
+ WRITE_INT32(fp, strlen(str)); \
+ tmpint32 = ntohl(tmpint32); \
+ if (tmpint32 > 0) \
+ { \
+ WRITE(fp, (str), tmpint32); \
+ } \
+ }
+
+#define WRITE_STRINGLIST(fp, arr) \
+ if ((arr) == NULL) \
+ { \
+ DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", 0); \
+ WRITE_INT32(fp, 0); \
+ } \
+ else \
+ { \
+ /* first determine length of array */ \
+ for (tmp3int32 = 0; (arr)[tmp3int32] != NULL; tmp3int32++) \
+ /* noting */ ; \
+ /* write number of strings */ \
+ DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \
+ WRITE_INT32(fp, tmp3int32); \
+ /* write strings */ \
+ for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \
+ { \
+ WRITE_STRING(fp, (arr)[tmp2int32]); \
+ } \
+ }
+
+#define WRITE_STRINGLIST_EXCEPT(fp, arr, not) \
+ /* first determine length of array */ \
+ tmp3int32 = 0; \
+ for (tmp2int32 = 0; (arr)[tmp2int32] != NULL; tmp2int32++) \
+ if (strcmp((arr)[tmp2int32], (not)) != 0) \
+ tmp3int32++; \
+ /* write number of strings (mius one because we intend to skip one) */ \
+ DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \
+ WRITE_INT32(fp, tmp3int32); \
+ /* write strings */ \
+ for (tmp2int32 = 0; (arr)[tmp2int32] != NULL; tmp2int32++) \
+ { \
+ if (strcmp((arr)[tmp2int32], (not)) != 0) \
+ { \
+ WRITE_STRING(fp, (arr)[tmp2int32]); \
+ } \
+ }
+
+/* READ macros, used for reading data, on read error they will
+ call the ERROR_OUT_READERROR or ERROR_OUT_BUFERROR macro
+ these macros may require the availability of the following
+ variables:
+ int32_t tmpint32; - temporary variable
+ */
+
+#define READ(fp, ptr, size) \
+ if (tio_read(fp, ptr, (size_t)size)) \
+ { \
+ char ebuf[128]; \
+ int saved_errno = errno; \
+ DEBUG_PRINT("READ : var="__STRING(ptr)" error: %s", \
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf))); \
+ ERROR_OUT_READERROR(fp); \
+ } \
+ DEBUG_PRINT("READ : var="__STRING(ptr)" size=%d", (int)(size)); \
+ DEBUG_DUMP(ptr, size);
+
+#define READ_INT32(fp, i) \
+ READ(fp, &tmpint32, sizeof(int32_t)); \
+ (i) = (int32_t)ntohl(tmpint32); \
+ DEBUG_PRINT("READ_INT32 : var="__STRING(i)" int32==%08x", (int)(i));
+
+/* read a string in a fixed-size "normal" buffer */
+#define READ_STRING(fp, buffer) \
+ /* read the size of the string */ \
+ READ(fp, &tmpint32, sizeof(int32_t)); \
+ tmpint32 = ntohl(tmpint32); \
+ DEBUG_PRINT("READ_STRING: var="__STRING(buffer)" strlen=%d", tmpint32); \
+ /* check if read would fit */ \
+ if (((size_t)tmpint32) >= sizeof(buffer)) \
+ { \
+ /* will not fit */ \
+ tmpint32 = (tmpint32 - sizeof(buffer)) + 1; \
+ DEBUG_PRINT("READ : buffer %d bytes too small", tmpint32); \
+ ERROR_OUT_BUFERROR(fp); \
+ } \
+ /* read string from the stream */ \
+ if (tmpint32 > 0) \
+ { \
+ READ(fp, buffer, (size_t)tmpint32); \
+ } \
+ /* null-terminate string in buffer */ \
+ buffer[tmpint32] = '\0'; \
+ DEBUG_PRINT("READ_STRING: var="__STRING(buffer)" string=\"%s\"", buffer);
+
+
+/* READ BUF macros that read data into a pre-allocated buffer.
+ these macros may require the availability of the following
+ variables:
+ int32_t tmpint32; - temporary variable
+ char *buffer; - pointer to a buffer for reading strings
+ size_t buflen; - the size of the buffer
+ size_t bufptr; - the current position in the buffer
+ */
+
+/* current position in the buffer */
+#define BUF_CUR \
+ (buffer + bufptr)
+
+/* check that the buffer has sz bytes left in it */
+#define BUF_CHECK(fp, sz) \
+ if ((bufptr + (size_t)(sz)) > buflen) \
+ { \
+ /* will not fit */ \
+ tmpint32 = bufptr + (sz) - (buflen); \
+ DEBUG_PRINT("READ : buffer %d bytes too small", tmpint32); \
+ ERROR_OUT_BUFERROR(fp); \
+ }
+
+/* move the buffer pointer */
+#define BUF_SKIP(sz) \
+ bufptr += (size_t)(sz);
+
+/* move BUF_CUR forward so that it is aligned to the specified
+ type width */
+#define BUF_ALIGN(fp, type) \
+ /* figure out number of bytes to skip forward */ \
+ tmp2int32 = (sizeof(type) - ((BUF_CUR - (char *)NULL) % sizeof(type))) \
+ % sizeof(type); \
+ /* check and skip */ \
+ BUF_CHECK(fp, tmp2int32); \
+ BUF_SKIP(tmp2int32);
+
+/* allocate a piece of the buffer to store an array in */
+#define BUF_ALLOC(fp, ptr, type, num) \
+ /* align to the specified type width */ \
+ BUF_ALIGN(fp, type); \
+ /* check that we have enough room */ \
+ BUF_CHECK(fp, (size_t)(num) * sizeof(type)); \
+ /* store the pointer */ \
+ (ptr) = (type *)BUF_CUR; \
+ /* reserve the space */ \
+ BUF_SKIP((size_t)(num) * sizeof(type));
+
+/* read a binary blob into the buffer */
+#define READ_BUF(fp, ptr, sz) \
+ /* check that there is enough room and read */ \
+ BUF_CHECK(fp, sz); \
+ READ(fp, BUF_CUR, (size_t)sz); \
+ /* store pointer and skip */ \
+ (ptr) = BUF_CUR; \
+ BUF_SKIP(sz);
+
+/* read string in the buffer (using buffer, buflen and bufptr)
+ and store the actual location of the string in field */
+#define READ_BUF_STRING(fp, field) \
+ /* read the size of the string */ \
+ READ(fp, &tmpint32, sizeof(int32_t)); \
+ tmpint32 = ntohl(tmpint32); \
+ DEBUG_PRINT("READ_BUF_STRING: var="__STRING(field)" strlen=%d", tmpint32); \
+ /* check if read would fit */ \
+ BUF_CHECK(fp, tmpint32 + 1); \
+ /* read string from the stream */ \
+ if (tmpint32 > 0) \
+ { \
+ READ(fp, BUF_CUR, (size_t)tmpint32); \
+ } \
+ /* null-terminate string in buffer */ \
+ BUF_CUR[tmpint32] = '\0'; \
+ DEBUG_PRINT("READ_BUF_STRING: var="__STRING(field)" string=\"%s\"", BUF_CUR); \
+ /* prepare result */ \
+ (field) = BUF_CUR; \
+ BUF_SKIP(tmpint32 + 1);
+
+/* read an array from a stream and store it as a null-terminated
+ array list (size for the array is allocated) */
+#define READ_BUF_STRINGLIST(fp, arr) \
+ /* read the number of entries */ \
+ READ(fp, &tmp3int32, sizeof(int32_t)); \
+ tmp3int32 = ntohl(tmp3int32); \
+ DEBUG_PRINT("READ_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \
+ /* allocate room for *char[num + 1] */ \
+ BUF_ALLOC(fp, arr, char *, tmp3int32 + 1); \
+ /* read all entries */ \
+ for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \
+ { \
+ READ_BUF_STRING(fp, (arr)[tmp2int32]); \
+ } \
+ /* set last entry to NULL */ \
+ (arr)[tmp2int32] = NULL;
+
+
+/* SKIP macros for skipping over certain parts of the protocol stream. */
+
+/* skip a number of bytes forward */
+#define SKIP(fp, sz) \
+ DEBUG_PRINT("READ : skip %d bytes", (int)(sz)); \
+ /* read (skip) the specified number of bytes */ \
+ if (tio_skip(fp, sz)) \
+ { \
+ char ebuf[128]; \
+ int saved_errno = errno; \
+ DEBUG_PRINT("READ : skip error: %s", \
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf))); \
+ ERROR_OUT_READERROR(fp); \
+ }
+
+/* read a string from the stream but don't do anything with the result */
+#define SKIP_STRING(fp) \
+ /* read the size of the string */ \
+ READ(fp, &tmpint32, sizeof(int32_t)); \
+ tmpint32 = ntohl(tmpint32); \
+ DEBUG_PRINT("READ_STRING: skip %d bytes", (int)tmpint32); \
+ /* read (skip) the specified number of bytes */ \
+ SKIP(fp, tmpint32);
+
+/* skip a list of strings */
+#define SKIP_STRINGLIST(fp) \
+ /* read the number of entries */ \
+ READ(fp, &tmp3int32, sizeof(int32_t)); \
+ tmp3int32 = ntohl(tmp3int32); \
+ DEBUG_PRINT("READ_STRLST: skip %d strings", (int)tmp3int32); \
+ /* read all entries */ \
+ for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \
+ { \
+ SKIP_STRING(fp); \
+ }
+
+
+/* These are functions and macros for performing common operations in
+ the nslcd request/response protocol. */
+
+/* returns a socket to the server or NULL on error (see errno),
+ socket should be closed with tio_close() */
+TFILE *nslcd_client_open(void)
+ MUST_USE;
+
+/* generic request code */
+#define NSLCD_REQUEST(fp, action, writefn) \
+ /* open a client socket */ \
+ if ((fp = nslcd_client_open()) == NULL) \
+ { \
+ ERROR_OUT_OPENERROR; \
+ } \
+ /* write a request header with a request code */ \
+ WRITE_INT32(fp, (int32_t)NSLCD_VERSION) \
+ WRITE_INT32(fp, (int32_t)action) \
+ /* write the request parameters (if any) */ \
+ writefn; \
+ /* flush the stream */ \
+ if (tio_flush(fp) < 0) \
+ { \
+ char ebuf[128]; \
+ int saved_errno = errno; \
+ DEBUG_PRINT("WRITE_FLUSH : error: %s", \
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf))); \
+ ERROR_OUT_WRITEERROR(fp); \
+ } \
+ /* read and check response version number */ \
+ READ(fp, &tmpint32, sizeof(int32_t)); \
+ tmpint32 = ntohl(tmpint32); \
+ if (tmpint32 != (int32_t)NSLCD_VERSION) \
+ { \
+ ERROR_OUT_READERROR(fp); \
+ } \
+ /* read and check response request number */ \
+ READ(fp, &tmpint32, sizeof(int32_t)); \
+ tmpint32 = ntohl(tmpint32); \
+ if (tmpint32 != (int32_t)(action)) \
+ { \
+ ERROR_OUT_READERROR(fp); \
+ }
+
+/* Read the response code (the result code of the query) from
+ the stream. */
+#define READ_RESPONSE_CODE(fp) \
+ READ(fp, &tmpint32, sizeof(int32_t)); \
+ tmpint32 = ntohl(tmpint32); \
+ if (tmpint32 != (int32_t)NSLCD_RESULT_BEGIN) \
+ { \
+ ERROR_OUT_NOSUCCESS(fp); \
+ }
+
+#endif /* not COMMON__NSLCD_PROT_H */
diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd.h b/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd.h
new file mode 100644
index 0000000..c7dc013
--- /dev/null
+++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd.h
@@ -0,0 +1,305 @@
+/*
+ nslcd.h - file describing client/server protocol
+
+ Copyright (C) 2006 West Consulting
+ Copyright (C) 2006, 2007, 2009, 2010, 2011, 2012, 2013 Arthur de Jong
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA
+*/
+
+#ifndef _NSLCD_H
+#define _NSLCD_H 1
+
+/*
+ The protocol used between the nslcd client and server is a simple binary
+ protocol. It is request/response based where the client initiates a
+ connection, does a single request and closes the connection again. Any
+ mangled or not understood messages will be silently ignored by the server.
+
+ A request looks like:
+ INT32 NSLCD_VERSION
+ INT32 NSLCD_ACTION_*
+ [request parameters if any]
+ A response looks like:
+ INT32 NSLCD_VERSION
+ INT32 NSLCD_ACTION_* (the original request type)
+ [result(s)]
+ INT32 NSLCD_RESULT_END
+ A single result entry looks like:
+ INT32 NSLCD_RESULT_BEGIN
+ [result value(s)]
+ If a response would return multiple values (e.g. for NSLCD_ACTION_*_ALL
+ functions) each return value will be preceded by a NSLCD_RESULT_BEGIN
+ value. After the last returned result the server sends
+ NSLCD_RESULT_END. If some error occurs (e.g. LDAP server unavailable,
+ error in the request, etc) the server terminates the connection to signal
+ an error condition (breaking the protocol).
+
+ These are the available basic data types:
+ INT32 - 32-bit integer value
+ TYPE - a typed field that is transferred using sizeof()
+ STRING - a string length (32bit) followed by the string value (not
+ null-terminated) the string itself is assumed to be UTF-8
+ STRINGLIST - a 32-bit number noting the number of strings followed by
+ the strings one at a time
+
+ Furthermore the ADDRESS compound data type is defined as:
+ INT32 type of address: e.g. AF_INET or AF_INET6
+ INT32 length of address
+ RAW the address itself
+ With the ADDRESSLIST using the same construct as with STRINGLIST.
+
+ The protocol uses network byte order for all types.
+*/
+
+/* The current version of the protocol. This protocol should only be
+ updated with major backwards-incompatible changes. */
+#define NSLCD_VERSION 0x00000002
+
+/* Get a NSLCD configuration option. There is one request parameter:
+ INT32 NSLCD_CONFIG_*
+ the result value is:
+ STRING value, interpretation depending on request */
+#define NSLCD_ACTION_CONFIG_GET 0x00010001
+
+/* return the message, if any, that is presented to the user when password
+ modification through PAM is prohibited */
+#define NSLCD_CONFIG_PAM_PASSWORD_PROHIBIT_MESSAGE 1
+
+/* Email alias (/etc/aliases) NSS requests. The result values for a
+ single entry are:
+ STRING alias name
+ STRINGLIST alias rcpts */
+#define NSLCD_ACTION_ALIAS_BYNAME 0x00020001
+#define NSLCD_ACTION_ALIAS_ALL 0x00020008
+
+/* Ethernet address/name mapping NSS requests. The result values for a
+ single entry are:
+ STRING ether name
+ TYPE(uint8_t[6]) ether address */
+#define NSLCD_ACTION_ETHER_BYNAME 0x00030001
+#define NSLCD_ACTION_ETHER_BYETHER 0x00030002
+#define NSLCD_ACTION_ETHER_ALL 0x00030008
+
+/* Group and group membership related NSS requests. The result values
+ for a single entry are:
+ STRING group name
+ STRING group password
+ INT32 group id
+ STRINGLIST members (usernames) of the group
+ (not that the BYMEMER call returns an empty members list) */
+#define NSLCD_ACTION_GROUP_BYNAME 0x00040001
+#define NSLCD_ACTION_GROUP_BYGID 0x00040002
+#define NSLCD_ACTION_GROUP_BYMEMBER 0x00040006
+#define NSLCD_ACTION_GROUP_ALL 0x00040008
+
+/* Hostname (/etc/hosts) lookup NSS requests. The result values
+ for an entry are:
+ STRING host name
+ STRINGLIST host aliases
+ ADDRESSLIST host addresses */
+#define NSLCD_ACTION_HOST_BYNAME 0x00050001
+#define NSLCD_ACTION_HOST_BYADDR 0x00050002
+#define NSLCD_ACTION_HOST_ALL 0x00050008
+
+/* Netgroup NSS result entries contain a number of parts. A result entry
+ starts with:
+ STRING netgroup name
+ followed by zero or more references to other netgroups or netgroup
+ triples. A reference to another netgroup looks like:
+ INT32 NSLCD_NETGROUP_TYPE_NETGROUP
+ STRING other netgroup name
+ A a netgroup triple looks like:
+ INT32 NSLCD_NETGROUP_TYPE_TRIPLE
+ STRING host
+ STRING user
+ STRING domain
+ A netgroup result entry is terminated by:
+ INT32 NSLCD_NETGROUP_TYPE_END
+ */
+#define NSLCD_ACTION_NETGROUP_BYNAME 0x00060001
+#define NSLCD_ACTION_NETGROUP_ALL 0x00060008
+#define NSLCD_NETGROUP_TYPE_NETGROUP 1
+#define NSLCD_NETGROUP_TYPE_TRIPLE 2
+#define NSLCD_NETGROUP_TYPE_END 3
+
+/* Network name (/etc/networks) NSS requests. Result values for a single
+ entry are:
+ STRING network name
+ STRINGLIST network aliases
+ ADDRESSLIST network addresses */
+#define NSLCD_ACTION_NETWORK_BYNAME 0x00070001
+#define NSLCD_ACTION_NETWORK_BYADDR 0x00070002
+#define NSLCD_ACTION_NETWORK_ALL 0x00070008
+
+/* User account (/etc/passwd) NSS requests. Result values are:
+ STRING user name
+ STRING user password
+ INT32 user id
+ INT32 group id
+ STRING gecos information
+ STRING home directory
+ STRING login shell */
+#define NSLCD_ACTION_PASSWD_BYNAME 0x00080001
+#define NSLCD_ACTION_PASSWD_BYUID 0x00080002
+#define NSLCD_ACTION_PASSWD_ALL 0x00080008
+
+/* Protocol information requests. Result values are:
+ STRING protocol name
+ STRINGLIST protocol aliases
+ INT32 protocol number */
+#define NSLCD_ACTION_PROTOCOL_BYNAME 0x00090001
+#define NSLCD_ACTION_PROTOCOL_BYNUMBER 0x00090002
+#define NSLCD_ACTION_PROTOCOL_ALL 0x00090008
+
+/* RPC information requests. Result values are:
+ STRING rpc name
+ STRINGLIST rpc aliases
+ INT32 rpc number */
+#define NSLCD_ACTION_RPC_BYNAME 0x000a0001
+#define NSLCD_ACTION_RPC_BYNUMBER 0x000a0002
+#define NSLCD_ACTION_RPC_ALL 0x000a0008
+
+/* Service (/etc/services) information requests. The BYNAME and BYNUMBER
+ requests contain an extra protocol string in the request which, if not
+ blank, will filter the services by this protocol. Result values are:
+ STRING service name
+ STRINGLIST service aliases
+ INT32 service (port) number
+ STRING service protocol */
+#define NSLCD_ACTION_SERVICE_BYNAME 0x000b0001
+#define NSLCD_ACTION_SERVICE_BYNUMBER 0x000b0002
+#define NSLCD_ACTION_SERVICE_ALL 0x000b0008
+
+/* Extended user account (/etc/shadow) information requests. Result
+ values for a single entry are:
+ STRING user name
+ STRING user password
+ INT32 last password change
+ INT32 mindays
+ INT32 maxdays
+ INT32 warn
+ INT32 inact
+ INT32 expire
+ INT32 flag */
+#define NSLCD_ACTION_SHADOW_BYNAME 0x000c0001
+#define NSLCD_ACTION_SHADOW_ALL 0x000c0008
+
+/* PAM-related requests. The request parameters for all these requests
+ begin with:
+ STRING user name
+ STRING service name
+ STRING ruser
+ STRING rhost
+ STRING tty
+ If the user is not known in LDAP no result may be returned (immediately
+ return NSLCD_RESULT_END instead of a PAM error code). */
+
+/* PAM authentication check request. The extra request values are:
+ STRING password
+ and the result value consists of:
+ INT32 authc NSLCD_PAM_* result code
+ STRING user name (the canonical user name)
+ INT32 authz NSLCD_PAM_* result code
+ STRING authorisation error message
+ If the username is empty in this request an attempt is made to
+ authenticate as the administrator (set using rootpwmoddn).
+ Some authorisation checks are already done during authentication so the
+ response also includes authorisation information. */
+#define NSLCD_ACTION_PAM_AUTHC 0x000d0001
+
+/* PAM authorisation check request. The result value consists of:
+ INT32 authz NSLCD_PAM_* result code
+ STRING authorisation error message
+ The authentication check may have already returned some authorisation
+ information. The authorisation error message, if supplied, will be used
+ by the PAM module instead of a message that is generated by the PAM
+ module itself. */
+#define NSLCD_ACTION_PAM_AUTHZ 0x000d0002
+
+/* PAM session open request. The result value consists of:
+ STRING session id
+ This session id may be used to close this session with. */
+#define NSLCD_ACTION_PAM_SESS_O 0x000d0003
+
+/* PAM session close request. This request has the following
+ extra request value:
+ STRING session id
+ and this calls only returns an empty response value. */
+#define NSLCD_ACTION_PAM_SESS_C 0x000d0004
+
+/* PAM password modification request. This requests has the following extra
+ request values:
+ INT32 asroot: 0=oldpasswd is user passwd, 1=oldpasswd is root passwd
+ STRING old password
+ STRING new password
+ and returns there extra result values:
+ INT32 NSLCD_PAM_* result code
+ STRING error message */
+#define NSLCD_ACTION_PAM_PWMOD 0x000d0005
+
+/* User information change request. This request allows one to change
+ their full name and other information. The request parameters for this
+ request are:
+ STRING user name
+ INT32 asroot: 0=passwd is user passwd, 1=passwd is root passwd
+ STRING password
+ followed by one or more of the below, terminated by NSLCD_USERMOD_END
+ INT32 NSLCD_USERMOD_*
+ STRING new value
+ the response consists of one or more of the entries below, terminated
+ by NSLCD_USERMOD_END:
+ INT32 NSLCD_USERMOD_*
+ STRING response
+ (if the response is blank, the change went OK, otherwise the string
+ contains an error message)
+ */
+#define NSLCD_ACTION_USERMOD 0x000e0001
+
+/* These are the possible values for the NSLCD_ACTION_USERMOD operation
+ above. */
+#define NSLCD_USERMOD_END 0 /* end of change values */
+#define NSLCD_USERMOD_RESULT 1 /* global result value */
+#define NSLCD_USERMOD_FULLNAME 2 /* full name */
+#define NSLCD_USERMOD_ROOMNUMBER 3 /* room number */
+#define NSLCD_USERMOD_WORKPHONE 4 /* office phone number */
+#define NSLCD_USERMOD_HOMEPHONE 5 /* home phone number */
+#define NSLCD_USERMOD_OTHER 6 /* other info */
+#define NSLCD_USERMOD_HOMEDIR 7 /* home directory */
+#define NSLCD_USERMOD_SHELL 8 /* login shell */
+
+/* Request result codes. */
+#define NSLCD_RESULT_BEGIN 1
+#define NSLCD_RESULT_END 2
+
+/* Partial list of PAM result codes. */
+#define NSLCD_PAM_SUCCESS 0 /* everything ok */
+#define NSLCD_PAM_PERM_DENIED 6 /* Permission denied */
+#define NSLCD_PAM_AUTH_ERR 7 /* Authc failure */
+#define NSLCD_PAM_CRED_INSUFFICIENT 8 /* Cannot access authc data */
+#define NSLCD_PAM_AUTHINFO_UNAVAIL 9 /* Cannot retrieve authc info */
+#define NSLCD_PAM_USER_UNKNOWN 10 /* User not known */
+#define NSLCD_PAM_MAXTRIES 11 /* Retry limit reached */
+#define NSLCD_PAM_NEW_AUTHTOK_REQD 12 /* Password expired */
+#define NSLCD_PAM_ACCT_EXPIRED 13 /* Account expired */
+#define NSLCD_PAM_SESSION_ERR 14 /* Cannot make/remove session record */
+#define NSLCD_PAM_AUTHTOK_ERR 20 /* Authentication token manipulation error */
+#define NSLCD_PAM_AUTHTOK_DISABLE_AGING 23 /* Password aging disabled */
+#define NSLCD_PAM_IGNORE 25 /* Ignore module */
+#define NSLCD_PAM_ABORT 26 /* Fatal error */
+#define NSLCD_PAM_AUTHTOK_EXPIRED 27 /* authentication token has expired */
+
+#endif /* not _NSLCD_H */
diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.c b/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.c
new file mode 100644
index 0000000..012e680
--- /dev/null
+++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.c
@@ -0,0 +1,520 @@
+/*
+ tio.c - timed io functions
+ This file is part of the nss-pam-ldapd library.
+
+ Copyright (C) 2007-2014 Arthur de Jong
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA
+*/
+
+#include "portable.h"
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif /* HAVE_STDINT_H */
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <stdio.h>
+#include <limits.h>
+#include <poll.h>
+#include <time.h>
+
+#include "tio.h"
+
+/* for platforms that don't have ETIME use ETIMEDOUT */
+#ifndef ETIME
+#define ETIME ETIMEDOUT
+#endif /* ETIME */
+
+/* structure that holds a buffer
+ the buffer contains the data that is between the application and the
+ file descriptor that is used for efficient transfer
+ the buffer is built up as follows:
+ |.....********......|
+ ^start ^size
+ ^--len--^ */
+struct tio_buffer {
+ uint8_t *buffer;
+ size_t size; /* the size of the buffer */
+ size_t maxsize; /* the maximum size of the buffer */
+ size_t start; /* the start of the data (before start is unused) */
+ size_t len; /* size of the data (from the start) */
+};
+
+/* structure that holds all the state for files */
+struct tio_fileinfo {
+ int fd;
+ struct tio_buffer readbuffer;
+ struct tio_buffer writebuffer;
+ int readtimeout;
+ int writetimeout;
+ int read_resettable; /* whether the tio_reset() function can be called */
+#ifdef DEBUG_TIO_STATS
+ /* this is used to collect statistics on the use of the streams
+ and can be used to tune the buffer sizes */
+ size_t byteswritten;
+ size_t bytesread;
+#endif /* DEBUG_TIO_STATS */
+};
+
+/* some older versions of Solaris don't provide CLOCK_MONOTONIC but do have
+ a CLOCK_HIGHRES that has the same properties we need */
+#ifndef CLOCK_MONOTONIC
+#ifdef CLOCK_HIGHRES
+#define CLOCK_MONOTONIC CLOCK_HIGHRES
+#endif /* CLOCK_HIGHRES */
+#endif /* not CLOCK_MONOTONIC */
+
+/* update the timeout to the value that is remaining before the deadline
+ returns the number of milliseconds before the deadline (or a negative
+ value of the deadline has expired) */
+static inline int tio_time_remaining(struct timespec *deadline, int timeout)
+{
+ struct timespec tv;
+ /* if this is the first call, set the deadline and return the full time */
+ if ((deadline->tv_sec == 0) && (deadline->tv_nsec == 0))
+ {
+ if (clock_gettime(CLOCK_MONOTONIC, deadline) == 0)
+ {
+ deadline->tv_sec += timeout / 1000;
+ deadline->tv_nsec += (timeout % 1000) * 1000000;
+ }
+ return timeout;
+ }
+ /* get the current time (fall back to full time on error) */
+ if (clock_gettime(CLOCK_MONOTONIC, &tv))
+ return timeout;
+ /* calculate time remaining in milliseconds */
+ return (deadline->tv_sec - tv.tv_sec) * 1000 +
+ (deadline->tv_nsec - tv.tv_nsec) / 1000000;
+}
+
+/* open a new TFILE based on the file descriptor */
+TFILE *tio_fdopen(int fd, int readtimeout, int writetimeout,
+ size_t initreadsize, size_t maxreadsize,
+ size_t initwritesize, size_t maxwritesize)
+{
+ struct tio_fileinfo *fp;
+ fp = (struct tio_fileinfo *)malloc(sizeof(struct tio_fileinfo));
+ if (fp == NULL)
+ return NULL;
+ fp->fd = fd;
+ /* initialize read buffer */
+ fp->readbuffer.buffer = (uint8_t *)malloc(initreadsize);
+ if (fp->readbuffer.buffer == NULL)
+ {
+ free(fp);
+ return NULL;
+ }
+ fp->readbuffer.size = initreadsize;
+ fp->readbuffer.maxsize = maxreadsize;
+ fp->readbuffer.start = 0;
+ fp->readbuffer.len = 0;
+ /* initialize write buffer */
+ fp->writebuffer.buffer = (uint8_t *)malloc(initwritesize);
+ if (fp->writebuffer.buffer == NULL)
+ {
+ free(fp->readbuffer.buffer);
+ free(fp);
+ return NULL;
+ }
+ fp->writebuffer.size = initwritesize;
+ fp->writebuffer.maxsize = maxwritesize;
+ fp->writebuffer.start = 0;
+ fp->writebuffer.len = 0;
+ /* initialize other attributes */
+ fp->readtimeout = readtimeout;
+ fp->writetimeout = writetimeout;
+ fp->read_resettable = 0;
+#ifdef DEBUG_TIO_STATS
+ fp->byteswritten = 0;
+ fp->bytesread = 0;
+#endif /* DEBUG_TIO_STATS */
+ return fp;
+}
+
+/* wait for any activity on the specified file descriptor using
+ the specified deadline */
+static int tio_wait(int fd, short events, int timeout,
+ struct timespec *deadline)
+{
+ int t;
+ struct pollfd fds[1];
+ int rv;
+ while (1)
+ {
+ fds[0].fd = fd;
+ fds[0].events = events;
+ /* figure out the time we need to wait */
+ if ((t = tio_time_remaining(deadline, timeout)) < 0)
+ {
+ errno = ETIME;
+ return -1;
+ }
+ /* sanity check for moving clock */
+ if (t > timeout)
+ t = timeout;
+ /* wait for activity */
+ rv = poll(fds, 1, t);
+ if (rv > 0)
+ return 0; /* we have activity */
+ else if (rv == 0)
+ {
+ /* no file descriptors were available within the specified time */
+ errno = ETIME;
+ return -1;
+ }
+ else if ((errno != EINTR) && (errno != EAGAIN))
+ /* some error occurred */
+ return -1;
+ /* we just try again on EINTR or EAGAIN */
+ }
+}
+
+/* do a read on the file descriptor, returning the data in the buffer
+ if no data was read in the specified time an error is returned */
+int tio_read(TFILE *fp, void *buf, size_t count)
+{
+ struct timespec deadline = {0, 0};
+ int rv;
+ uint8_t *tmp;
+ size_t newsz;
+ size_t len;
+ /* have a more convenient storage type for the buffer */
+ uint8_t *ptr = (uint8_t *)buf;
+ /* loop until we have returned all the needed data */
+ while (1)
+ {
+ /* check if we have enough data in the buffer */
+ if (fp->readbuffer.len >= count)
+ {
+ if (count > 0)
+ {
+ if (ptr != NULL)
+ memcpy(ptr, fp->readbuffer.buffer + fp->readbuffer.start, count);
+ /* adjust buffer position */
+ fp->readbuffer.start += count;
+ fp->readbuffer.len -= count;
+ }
+ return 0;
+ }
+ /* empty what we have and continue from there */
+ if (fp->readbuffer.len > 0)
+ {
+ if (ptr != NULL)
+ {
+ memcpy(ptr, fp->readbuffer.buffer + fp->readbuffer.start,
+ fp->readbuffer.len);
+ ptr += fp->readbuffer.len;
+ }
+ count -= fp->readbuffer.len;
+ fp->readbuffer.start += fp->readbuffer.len;
+ fp->readbuffer.len = 0;
+ }
+ /* after this point until the read fp->readbuffer.len is 0 */
+ if (!fp->read_resettable)
+ {
+ /* the stream is not resettable, re-use the buffer */
+ fp->readbuffer.start = 0;
+ }
+ else if (fp->readbuffer.start >= (fp->readbuffer.size - 4))
+ {
+ /* buffer is running empty, try to grow buffer */
+ if (fp->readbuffer.size < fp->readbuffer.maxsize)
+ {
+ newsz = fp->readbuffer.size * 2;
+ if (newsz > fp->readbuffer.maxsize)
+ newsz = fp->readbuffer.maxsize;
+ tmp = realloc(fp->readbuffer.buffer, newsz);
+ if (tmp != NULL)
+ {
+ fp->readbuffer.buffer = tmp;
+ fp->readbuffer.size = newsz;
+ }
+ }
+ /* if buffer still does not contain enough room, clear resettable */
+ if (fp->readbuffer.start >= (fp->readbuffer.size - 4))
+ {
+ fp->readbuffer.start = 0;
+ fp->read_resettable = 0;
+ }
+ }
+ /* wait until we have input */
+ if (tio_wait(fp->fd, POLLIN, fp->readtimeout, &deadline))
+ return -1;
+ /* read the input in the buffer */
+ len = fp->readbuffer.size - fp->readbuffer.start;
+#ifdef SSIZE_MAX
+ if (len > SSIZE_MAX)
+ len = SSIZE_MAX;
+#endif /* SSIZE_MAX */
+ rv = read(fp->fd, fp->readbuffer.buffer + fp->readbuffer.start, len);
+ /* check for errors */
+ if (rv == 0)
+ {
+ errno = ECONNRESET;
+ return -1;
+ }
+ else if ((rv < 0) && (errno != EINTR) && (errno != EAGAIN))
+ return -1; /* something went wrong with the read */
+ else if (rv > 0)
+ fp->readbuffer.len = rv; /* skip the read part in the buffer */
+#ifdef DEBUG_TIO_STATS
+ fp->bytesread += rv;
+#endif /* DEBUG_TIO_STATS */
+ }
+}
+
+/* Read and discard the specified number of bytes from the stream. */
+int tio_skip(TFILE *fp, size_t count)
+{
+ return tio_read(fp, NULL, count);
+}
+
+/* Read all available data from the stream and empty the read buffer. */
+int tio_skipall(TFILE *fp, int timeout)
+{
+ struct timespec deadline = {0, 0};
+ int rv;
+ size_t len;
+ /* clear the read buffer */
+ fp->readbuffer.start = 0;
+ fp->readbuffer.len = 0;
+ fp->read_resettable = 0;
+ /* read until we can't read no more */
+ len = fp->readbuffer.size;
+#ifdef SSIZE_MAX
+ if (len > SSIZE_MAX)
+ len = SSIZE_MAX;
+#endif /* SSIZE_MAX */
+ while (1)
+ {
+ /* wait until we have input */
+ if (tio_wait(fp->fd, POLLIN, timeout, &deadline))
+ return -1;
+ /* read data from the stream */
+ rv = read(fp->fd, fp->readbuffer.buffer, len);
+ if (rv == 0)
+ return 0; /* end-of-file */
+ if ((rv < 0) && (errno == EWOULDBLOCK))
+ return 0; /* we've ready everything we can without blocking */
+ if ((rv < 0) && (errno != EINTR) && (errno != EAGAIN))
+ return -1; /* something went wrong with the read */
+ }
+}
+
+/* the caller has assured us that we can write to the file descriptor
+ and we give it a shot */
+static int tio_writebuf(TFILE *fp)
+{
+ int rv;
+ /* write the buffer */
+#ifdef MSG_NOSIGNAL
+ rv = send(fp->fd, fp->writebuffer.buffer + fp->writebuffer.start,
+ fp->writebuffer.len, MSG_NOSIGNAL);
+#else /* not MSG_NOSIGNAL */
+ /* on platforms that cannot use send() with masked signals, we change the
+ signal mask and change it back after the write (note that there is a
+ race condition here) */
+ struct sigaction act, oldact;
+ /* set up sigaction */
+ memset(&act, 0, sizeof(struct sigaction));
+ act.sa_sigaction = NULL;
+ act.sa_handler = SIG_IGN;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+ /* ignore SIGPIPE */
+ if (sigaction(SIGPIPE, &act, &oldact) != 0)
+ return -1; /* error setting signal handler */
+ /* write the buffer */
+ rv = write(fp->fd, fp->writebuffer.buffer + fp->writebuffer.start,
+ fp->writebuffer.len);
+ /* restore the old handler for SIGPIPE */
+ if (sigaction(SIGPIPE, &oldact, NULL) != 0)
+ return -1; /* error restoring signal handler */
+#endif
+ /* check for errors */
+ if ((rv == 0) || ((rv < 0) && (errno != EINTR) && (errno != EAGAIN)))
+ return -1; /* something went wrong with the write */
+ /* skip the written part in the buffer */
+ if (rv > 0)
+ {
+ fp->writebuffer.start += rv;
+ fp->writebuffer.len -= rv;
+#ifdef DEBUG_TIO_STATS
+ fp->byteswritten += rv;
+#endif /* DEBUG_TIO_STATS */
+ /* reset start if len is 0 */
+ if (fp->writebuffer.len == 0)
+ fp->writebuffer.start = 0;
+ /* move contents of the buffer to the front if it will save enough room */
+ if (fp->writebuffer.start >= (fp->writebuffer.size / 4))
+ {
+ memmove(fp->writebuffer.buffer,
+ fp->writebuffer.buffer + fp->writebuffer.start,
+ fp->writebuffer.len);
+ fp->writebuffer.start = 0;
+ }
+ }
+ return 0;
+}
+
+/* write all the data in the buffer to the stream */
+int tio_flush(TFILE *fp)
+{
+ struct timespec deadline = {0, 0};
+ /* loop until we have written our buffer */
+ while (fp->writebuffer.len > 0)
+ {
+ /* wait until we can write */
+ if (tio_wait(fp->fd, POLLOUT, fp->writetimeout, &deadline))
+ return -1;
+ /* write one block */
+ if (tio_writebuf(fp))
+ return -1;
+ }
+ return 0;
+}
+
+/* try a single write of data in the buffer if the file descriptor
+ will accept data */
+static int tio_flush_nonblock(TFILE *fp)
+{
+ struct pollfd fds[1];
+ int rv;
+ /* see if we can write without blocking */
+ fds[0].fd = fp->fd;
+ fds[0].events = POLLOUT;
+ rv = poll(fds, 1, 0);
+ /* check if any file descriptors were ready (timeout) or we were
+ interrupted */
+ if ((rv == 0) || ((rv < 0) && ((errno == EINTR) || (errno == EAGAIN))))
+ return 0;
+ /* any other errors? */
+ if (rv < 0)
+ return -1;
+ /* so file descriptor will accept writes */
+ return tio_writebuf(fp);
+}
+
+int tio_write(TFILE *fp, const void *buf, size_t count)
+{
+ size_t fr;
+ uint8_t *tmp;
+ size_t newsz;
+ const uint8_t *ptr = (const uint8_t *)buf;
+ /* keep filling the buffer until we have buffered everything */
+ while (count > 0)
+ {
+ /* figure out free size in buffer */
+ fr = fp->writebuffer.size - (fp->writebuffer.start + fp->writebuffer.len);
+ if (count <= fr)
+ {
+ /* the data fits in the buffer */
+ memcpy(fp->writebuffer.buffer + fp->writebuffer.start +
+ fp->writebuffer.len, ptr, count);
+ fp->writebuffer.len += count;
+ return 0;
+ }
+ else if (fr > 0)
+ {
+ /* fill the buffer with data that will fit */
+ memcpy(fp->writebuffer.buffer + fp->writebuffer.start +
+ fp->writebuffer.len, ptr, fr);
+ fp->writebuffer.len += fr;
+ ptr += fr;
+ count -= fr;
+ }
+ /* try to flush some of the data that is in the buffer */
+ if (tio_flush_nonblock(fp))
+ return -1;
+ /* if we have room now, try again */
+ if (fp->writebuffer.size > (fp->writebuffer.start + fp->writebuffer.len))
+ continue;
+ /* try to grow the buffer */
+ if (fp->writebuffer.size < fp->writebuffer.maxsize)
+ {
+ newsz = fp->writebuffer.size * 2;
+ if (newsz > fp->writebuffer.maxsize)
+ newsz = fp->writebuffer.maxsize;
+ tmp = realloc(fp->writebuffer.buffer, newsz);
+ if (tmp != NULL)
+ {
+ fp->writebuffer.buffer = tmp;
+ fp->writebuffer.size = newsz;
+ continue; /* try again */
+ }
+ }
+ /* write the buffer to the stream */
+ if (tio_flush(fp))
+ return -1;
+ }
+ return 0;
+}
+
+int tio_close(TFILE *fp)
+{
+ int retv;
+ /* write any buffered data */
+ retv = tio_flush(fp);
+#ifdef DEBUG_TIO_STATS
+ /* dump statistics to stderr */
+ fprintf(stderr, "DEBUG_TIO_STATS READ=%d WRITTEN=%d\n", fp->bytesread,
+ fp->byteswritten);
+#endif /* DEBUG_TIO_STATS */
+ /* close file descriptor */
+ if (close(fp->fd))
+ retv = -1;
+ /* free any allocated buffers */
+ memset(fp->readbuffer.buffer, 0, fp->readbuffer.size);
+ memset(fp->writebuffer.buffer, 0, fp->writebuffer.size);
+ free(fp->readbuffer.buffer);
+ free(fp->writebuffer.buffer);
+ /* free the tio struct itself */
+ free(fp);
+ /* return the result of the earlier operations */
+ return retv;
+}
+
+void tio_mark(TFILE *fp)
+{
+ /* move any data in the buffer to the start of the buffer */
+ if ((fp->readbuffer.start > 0) && (fp->readbuffer.len > 0))
+ {
+ memmove(fp->readbuffer.buffer,
+ fp->readbuffer.buffer + fp->readbuffer.start, fp->readbuffer.len);
+ fp->readbuffer.start = 0;
+ }
+ /* mark the stream as resettable */
+ fp->read_resettable = 1;
+}
+
+int tio_reset(TFILE *fp)
+{
+ /* check if the stream is (still) resettable */
+ if (!fp->read_resettable)
+ return -1;
+ /* reset the buffer */
+ fp->readbuffer.len += fp->readbuffer.start;
+ fp->readbuffer.start = 0;
+ return 0;
+}
diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.h b/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.h
new file mode 100644
index 0000000..95f9812
--- /dev/null
+++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.h
@@ -0,0 +1,83 @@
+/*
+ tio.h - timed io functions
+ This file is part of the nss-pam-ldapd library.
+
+ Copyright (C) 2007, 2008, 2010, 2012, 2013 Arthur de Jong
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA
+*/
+
+/*
+
+ TODO: Add some documentation here.
+
+ the SIGPIPE signal should be ignored (is ignored in this code)
+
+ This library is not thread safe. You cannot share TFILE objects between
+ threads and expect to be able to read and write from them in different
+ threads. All the state is in the TFILE object so calls to this library on
+ different objects can be done in parallel.
+
+*/
+
+#ifndef COMMON__TIO_H
+#define COMMON__TIO_H
+
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include "attrs.h"
+
+/* This is a generic file handle used for reading and writing
+ (something like FILE from stdio.h). */
+typedef struct tio_fileinfo TFILE;
+
+/* Open a new TFILE based on the file descriptor. The timeout is set for any
+ operation (value in milliseconds). */
+TFILE *tio_fdopen(int fd, int readtimeout, int writetimeout,
+ size_t initreadsize, size_t maxreadsize,
+ size_t initwritesize, size_t maxwritesize)
+ LIKE_MALLOC MUST_USE;
+
+/* Read the specified number of bytes from the stream. */
+int tio_read(TFILE *fp, void *buf, size_t count);
+
+/* Read and discard the specified number of bytes from the stream. */
+int tio_skip(TFILE *fp, size_t count);
+
+/* Read all available data from the stream and empty the read buffer. */
+int tio_skipall(TFILE *fp, int timeout);
+
+/* Write the specified buffer to the stream. */
+int tio_write(TFILE *fp, const void *buf, size_t count);
+
+/* Write out all buffered data to the stream. */
+int tio_flush(TFILE *fp);
+
+/* Flush the streams and closes the underlying file descriptor. */
+int tio_close(TFILE *fp);
+
+/* Store the current position in the stream so that we can jump back to it
+ with the tio_reset() function. */
+void tio_mark(TFILE *fp);
+
+/* Rewinds the stream to the point set by tio_mark(). Note that this only
+ resets the read stream and not the write stream. This function returns
+ whether the reset was successful (this function may fail if the buffers
+ were full). */
+int tio_reset(TFILE *fp);
+
+#endif /* COMMON__TIO_H */