diff options
Diffstat (limited to 'libraries/liblutil')
26 files changed, 6102 insertions, 0 deletions
diff --git a/libraries/liblutil/Makefile.in b/libraries/liblutil/Makefile.in new file mode 100644 index 0000000..fff1c76 --- /dev/null +++ b/libraries/liblutil/Makefile.in @@ -0,0 +1,53 @@ +# Makefile for -llutil +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2022 The OpenLDAP Foundation. +## 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 +## <http://www.OpenLDAP.org/license.html>. + +LIBRARY = liblutil.a + +LDAP_INCDIR= ../../include +LDAP_LIBDIR= ../../libraries + +NT_SRCS = ntservice.c +NT_OBJS = ntservice.o slapdmsg.res + +UNIX_SRCS = detach.c +UNIX_OBJS = detach.o + +XLIBS = $(LIBRARY) $(LDAP_LIBLBER_LA) + +SRCS = base64.c entropy.c sasl.c signal.c hash.c passfile.c \ + md5.c passwd.c sha1.c getpass.c lockf.c utils.c uuid.c sockpair.c \ + meter.c \ + @LIBSRCS@ $(@PLAT@_SRCS) + +OBJS = base64.o entropy.o sasl.o signal.o hash.o passfile.o \ + md5.o passwd.o sha1.o getpass.o lockf.o utils.o uuid.o sockpair.o \ + meter.o \ + @LIBOBJS@ $(@PLAT@_OBJS) + +# These rules are for a Mingw32 build, specifically. +# It's ok for them to be here because the clean rule is harmless, and +# slapdmsg.res won't get built unless it's declared in OBJS. + +RC = @RC@ + +slapdmsg.bin: FORCE + @if [ ! -f $@ ]; then cp $(srcdir)/$@ .; fi + +slapdmsg.res: slapdmsg.rc slapdmsg.bin + $(RC) $< -O coff -o $@ + +clean-local: + $(RM) *.res + diff --git a/libraries/liblutil/base64.c b/libraries/liblutil/base64.c new file mode 100644 index 0000000..3a148f0 --- /dev/null +++ b/libraries/liblutil/base64.c @@ -0,0 +1,308 @@ +/* base64.c -- routines to encode/decode base64 data */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * Portions Copyright 1998-2003 Kurt D. Zeilenga. + * Portions Copyright 1995 IBM Corporation. + * 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 + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1996, 1998 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ +/* This work is based upon Base64 routines (developed by IBM) found + * Berkeley Internet Name Daemon (BIND) as distributed by ISC. They + * were adapted for inclusion in OpenLDAP Software by Kurt D. Zeilenga. + */ + +#include "portable.h" + +#include <ac/assert.h> +#include <ac/stdlib.h> +#include <ac/ctype.h> +#include <ac/string.h> + +/* include socket.h to get sys/types.h and/or winsock2.h */ +#include <ac/socket.h> + +#include "lutil.h" + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) + The following encoding technique is taken from RFC 1521 by Borenstein + and Freed. It is reproduced here in a slightly edited form for + convenience. + + A 65-character subset of US-ASCII is used, enabling 6 bits to be + represented per printable character. (The extra 65th character, "=", + is used to signify a special processing function.) + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8-bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + + Each 6-bit group is used as an index into an array of 64 printable + characters. The character referenced by the index is placed in the + output string. + + Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a quantity. When fewer than 24 input + bits are available in an input group, zero bits are added (on the + right) to form an integral number of 6-bit groups. Padding at the + end of the data is performed using the '=' character. + + Since all base64 input is an integral number of octets, only the + ------------------------------------------------- + following cases can arise: + + (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded + output will be an integral multiple of 4 characters + with no "=" padding, + (2) the final quantum of encoding input is exactly 8 bits; + here, the final unit of encoded output will be two + characters followed by two "=" padding characters, or + (3) the final quantum of encoding input is exactly 16 bits; + here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + +int +lutil_b64_ntop( + unsigned char const *src, + size_t srclength, + char *target, + size_t targsize) +{ + size_t datalength = 0; + unsigned char input[3]; + unsigned char output[4]; + size_t i; + + while (2 < srclength) { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + srclength -= 3; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = input[2] & 0x3f; + assert(output[0] < 64); + assert(output[1] < 64); + assert(output[2] < 64); + assert(output[3] < 64); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + target[datalength++] = Base64[output[2]]; + target[datalength++] = Base64[output[3]]; + } + + /* Now we worry about padding. */ + if (0 != srclength) { + /* Get what's left. */ + input[0] = input[1] = input[2] = '\0'; + for (i = 0; i < srclength; i++) + input[i] = *src++; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + assert(output[0] < 64); + assert(output[1] < 64); + assert(output[2] < 64); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + if (srclength == 1) + target[datalength++] = Pad64; + else + target[datalength++] = Base64[output[2]]; + target[datalength++] = Pad64; + } + if (datalength >= targsize) + return (-1); + target[datalength] = '\0'; /* Returned value doesn't count \0. */ + return (datalength); +} + +/* skips all whitespace anywhere. + converts characters, four at a time, starting at (or after) + src from base - 64 numbers into three 8 bit bytes in the target area. + it returns the number of data bytes stored at the target, or -1 on error. + */ + +int +lutil_b64_pton( + char const *src, + unsigned char *target, + size_t targsize) +{ + int tarindex, state, ch; + char *pos; + + state = 0; + tarindex = 0; + + while ((ch = *src++) != '\0') { + if (isascii(ch) && isspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr(Base64, ch); + if (pos == 0) /* A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if ((size_t)tarindex >= targsize) + return (-1); + target[tarindex] = (pos - Base64) << 2; + } + state = 1; + break; + case 1: + if (target) { + if ((size_t)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex+1] = ((pos - Base64) & 0x0f) + << 4 ; + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if ((size_t)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex+1] = ((pos - Base64) & 0x03) + << 6; + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if ((size_t)tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + default: + abort(); + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (! (isascii(ch) && isspace(ch))) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (! (isascii(ch) && isspace(ch))) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); +} diff --git a/libraries/liblutil/detach.c b/libraries/liblutil/detach.c new file mode 100644 index 0000000..e939e76 --- /dev/null +++ b/libraries/liblutil/detach.c @@ -0,0 +1,144 @@ +/* detach.c -- routines to daemonize a process */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * 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 + * <http://www.OpenLDAP.org/license.html>. + */ +/* + * Copyright (c) 1990, 1994 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* This work was originally developed by the University of Michigan + * and distributed as part of U-MICH LDAP. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/signal.h> +#include <ac/socket.h> +#include <ac/unistd.h> + +#include <sys/stat.h> +#include <fcntl.h> + +#ifdef HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif + +#include "lutil.h" + +int +lutil_detach( int debug, int do_close ) +{ + int i, sd, nbits, pid; + +#ifdef HAVE_SYSCONF + nbits = sysconf( _SC_OPEN_MAX ); +#elif defined(HAVE_GETDTABLESIZE) + nbits = getdtablesize(); +#else + nbits = FD_SETSIZE; +#endif + +#ifdef FD_SETSIZE + if ( nbits > FD_SETSIZE ) { + nbits = FD_SETSIZE; + } +#endif /* FD_SETSIZE */ + + if ( debug == 0 ) { + for ( i = 0; i < 5; i++ ) { +#ifdef HAVE_THR + pid = fork1(); +#else + pid = fork(); +#endif + switch ( pid ) + { + case -1: + sleep( 5 ); + continue; + + case 0: + break; + + default: + return pid; + } + break; + } + + if ( (sd = open( "/dev/null", O_RDWR )) == -1 && + (sd = open( "/dev/null", O_RDONLY )) == -1 && + /* Panic -- open *something* */ + (sd = open( "/", O_RDONLY )) == -1 ) { + perror("/dev/null"); + } else { + /* redirect stdin, stdout, stderr to /dev/null */ + dup2( sd, STDIN_FILENO ); + dup2( sd, STDOUT_FILENO ); + dup2( sd, STDERR_FILENO ); + + switch( sd ) { + default: + close( sd ); + case STDIN_FILENO: + case STDOUT_FILENO: + case STDERR_FILENO: + break; + } + } + + if ( do_close ) { + /* close everything else */ + for ( i = 0; i < nbits; i++ ) { + if( i != STDIN_FILENO && + i != STDOUT_FILENO && + i != STDERR_FILENO ) + { + close( i ); + } + } + } + +#ifdef CHDIR_TO_ROOT + (void) chdir( "/" ); +#endif + +#ifdef HAVE_SETSID + (void) setsid(); +#elif defined(TIOCNOTTY) + if ( (sd = open( "/dev/tty", O_RDWR )) != -1 ) { + (void) ioctl( sd, TIOCNOTTY, NULL ); + (void) close( sd ); + } +#endif + } + +#ifdef SIGPIPE + (void) SIGNAL( SIGPIPE, SIG_IGN ); +#endif + return 0; +} diff --git a/libraries/liblutil/entropy.c b/libraries/liblutil/entropy.c new file mode 100644 index 0000000..289aca4 --- /dev/null +++ b/libraries/liblutil/entropy.c @@ -0,0 +1,170 @@ +/* entropy.c -- routines for providing pseudo-random data */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1999-2022 The OpenLDAP Foundation. + * Portions Copyright 1999-2003 Kurt D. Zeilenga. + * 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 + * <http://www.OpenLDAP.org/license.html>. + */ +/* This work was initially developed by Kurt D. Zeilenga for + * inclusion in OpenLDAP Software based, in part, on publicly + * available works (as noted below). + */ + +#include "portable.h" + +#include <ac/string.h> +#include <ac/time.h> +#include <ac/unistd.h> + +#ifdef HAVE_PROCESS_H +#include <process.h> +#endif + +#include <fcntl.h> + +#include <lutil.h> +#include <lutil_md5.h> + +/* + * lutil_entropy() provides nbytes of entropy in buf. + * Quality offered is suitable for one-time uses, such as "once" keys. + * Values may not be suitable for multi-time uses. + * + * Note: Callers are encouraged to provide additional bytes of + * of entropy in the buf argument. This information is used in + * fallback mode to improve the quality of bytes returned. + * + * This routinue should be extended to support additional sources + * of entropy. + */ +int lutil_entropy( unsigned char *buf, ber_len_t nbytes ) +{ + if( nbytes == 0 ) return 0; + +#ifdef URANDOM_DEVICE +#define URANDOM_NREADS 4 + /* Linux and *BSD offer a urandom device */ + { + int rc, fd, n=0; + + fd = open( URANDOM_DEVICE, O_RDONLY ); + + if( fd < 0 ) return -1; + + do { + rc = read( fd, buf, nbytes ); + if( rc <= 0 ) break; + + buf+=rc; + nbytes-=rc; + + if( ++n >= URANDOM_NREADS ) break; + } while( nbytes > 0 ); + + close(fd); + return nbytes > 0 ? -1 : 0; + } +#elif defined(PROV_RSA_FULL) + { + /* Not used since _WIN32_WINNT not set... */ + HCRYPTPROV hProv = 0; + + /* Get handle to user default provider */ + if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) { + return -1; + } + + /* Generate random initialization vector */ + if(!CryptGenRandom(hProv, (DWORD) nbytes, (BYTE *) buf)) { + return -1; + } + + /* Release provider handle */ + if(hProv != 0) CryptReleaseContext(hProv, 0); + + return 0; + } +#else + { + /* based upon Phil Karn's "practical randomness" idea + * but implementation 100% OpenLDAP. So don't blame Phil. + * + * Worse case is that this is a MD5 hash of a counter, if + * MD5 is a strong cryptographic hash, this should be fairly + * resistant to attack + */ + + /* + * the caller may need to provide external synchronization OR + * provide entropy (in buf) to ensure quality results as + * access to this counter may not be atomic. + */ + static int counter = 0; + ber_len_t n; + + struct rdata_s { + int counter; + + unsigned char *buf; + struct rdata_s *stack; + + pid_t pid; + +#ifdef HAVE_GETTIMEOFDAY + struct timeval tv; +#else + time_t time; +#endif + + unsigned long junk; /* purposely not initialized */ + } rdata; + + /* make sure rdata differs for each process */ + rdata.pid = getpid(); + + /* make sure rdata differs for each program */ + rdata.buf = buf; + rdata.stack = &rdata; + + for( n = 0; n < nbytes; n += 16 ) { + struct lutil_MD5Context ctx; + unsigned char digest[16]; + + /* poor resolution */ +#ifdef HAVE_GETTIMEOFDAY + (void) gettimeofday( &rdata.tv, NULL ); +#else + (void) time( &rdata.time ); +#endif + + /* make sure rdata differs */ + rdata.counter = ++counter; + rdata.pid++; + rdata.junk++; + + lutil_MD5Init( &ctx ); + lutil_MD5Update( &ctx, (unsigned char *) &rdata, sizeof( rdata ) ); + + /* allow caller to provided additional entropy */ + lutil_MD5Update( &ctx, buf, nbytes ); + + lutil_MD5Final( digest, &ctx ); + + AC_MEMCPY( &buf[n], digest, + nbytes - n >= 16 ? 16 : nbytes - n ); + } + + return 0; + } +#endif + return -1; +} diff --git a/libraries/liblutil/getopt.c b/libraries/liblutil/getopt.c new file mode 100644 index 0000000..bc3feba --- /dev/null +++ b/libraries/liblutil/getopt.c @@ -0,0 +1,136 @@ +/* getopt.c -- replacement getopt(3) routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * Portions Copyright 1998-2003 Kurt D. Zeilenga. + * 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 + * <http://www.OpenLDAP.org/license.html>. + */ +/* This work is based upon the public-domain getopt(3) routines + * developed by AT&T. Modified by Kurt D. Zeilenga for inclusion + * into OpenLDAP Software. Significant contributors include: + * Howard Chu + */ + +#include "portable.h" + +#ifndef HAVE_GETOPT + +#include <stdio.h> + +#include <ac/string.h> +#include <ac/unistd.h> + +#ifdef HAVE_IO_H +#include <io.h> +#endif + +#include "lutil.h" + +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif + +int opterr = 1; +int optind = 1; +int optopt; +char * optarg; + +#ifdef HAVE_EBCDIC +extern int _trans_argv; +#endif + +static void ERR (char * const argv[], const char * s, char c) +{ +#ifdef DF_TRACE_DEBUG +printf("DF_TRACE_DEBUG: static void ERR () in getopt.c\n"); +#endif + if (opterr) + { + char *ptr, outbuf[4096]; + + ptr = lutil_strncopy(outbuf, argv[0], sizeof(outbuf) - 2); + ptr = lutil_strncopy(ptr, s, sizeof(outbuf)-2 -(ptr-outbuf)); + *ptr++ = c; + *ptr++ = '\n'; +#ifdef HAVE_EBCDIC + __atoe_l(outbuf, ptr - outbuf); +#endif + (void) write(STDERR_FILENO,outbuf,ptr - outbuf); + } +} + +int getopt (int argc, char * const argv [], const char * opts) +{ + static int sp = 1, error = (int) '?'; + static char sw = '-', eos = '\0', arg = ':'; + register char c, * cp; + +#ifdef DF_TRACE_DEBUG +printf("DF_TRACE_DEBUG: int getopt () in getopt.c\n"); +#endif + +#ifdef HAVE_EBCDIC + if (_trans_argv) { + int i; + for (i=0; i<argc; i++) __etoa(argv[i]); + _trans_argv = 0; + } +#endif + if (sp == 1) + { + if (optind >= argc || argv[optind][0] != sw + || argv[optind][1] == eos) + return EOF; + else if (strcmp(argv[optind],"--") == 0) + { + optind++; + return EOF; + } + } + c = argv[optind][sp]; + optopt = (int) c; + if (c == arg || (cp = strchr(opts,c)) == NULL) + { + ERR(argv,_(": illegal option--"),c); + if (argv[optind][++sp] == eos) + { + optind++; + sp = 1; + } + return error; + } + else if (*++cp == arg) + { + if (argv[optind][sp + 1] != eos) + optarg = &argv[optind++][sp + 1]; + else if (++optind >= argc) + { + ERR(argv,_(": option requires an argument--"),c); + sp = 1; + return error; + } + else + optarg = argv[optind++]; + sp = 1; + } + else + { + if (argv[optind][++sp] == eos) + { + sp = 1; + optind++; + } + optarg = NULL; + } + return (int) c; +} +#endif /* HAVE_GETOPT */ diff --git a/libraries/liblutil/getpass.c b/libraries/liblutil/getpass.c new file mode 100644 index 0000000..e322723 --- /dev/null +++ b/libraries/liblutil/getpass.c @@ -0,0 +1,130 @@ +/* getpass.c -- get password from user */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * Portions Copyright 1998-2003 Kurt D. Zeilenga. + * Portions Copyright 2009 Howard Chu. + * 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 + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1992, 1993 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* This work was originally developed by the University of Michigan + * and distributed as part of U-MICH LDAP. It was adapted for use in + * -llutil by Kurt D. Zeilenga and subsequently rewritten by Howard Chu. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/ctype.h> +#include <ac/signal.h> +#include <ac/string.h> +#include <ac/termios.h> +#include <ac/time.h> +#include <ac/unistd.h> + +#ifndef HAVE_GETPASSPHRASE + +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#ifdef HAVE_CONIO_H +#include <conio.h> +#endif + +#include <lber.h> +#include <ldap.h> + +#include "ldap_defaults.h" + +#define PBUF 512 + +#ifdef HAVE_WINSOCK +#define TTY "con:" +#else +#define TTY "/dev/tty" +#endif + +char * +lutil_getpass( const char *prompt ) +{ + static char pbuf[PBUF]; + FILE *fi; + int c; + unsigned i; +#if defined(HAVE_TERMIOS_H) || defined(HAVE_SGTTY_H) + TERMIO_TYPE ttyb; + TERMFLAG_TYPE flags; + RETSIGTYPE (*sig)( int sig ); +#endif + + if( prompt == NULL ) prompt = _("Password: "); + +#ifdef DEBUG + if (debug & D_TRACE) + printf("->getpass(%s)\n", prompt); +#endif + +#if defined(HAVE_TERMIOS_H) || defined(HAVE_SGTTY_H) + if ((fi = fopen(TTY, "r")) == NULL) + fi = stdin; + else + setbuf(fi, (char *)NULL); + if (fi != stdin) { + if (GETATTR(fileno(fi), &ttyb) < 0) + perror("GETATTR"); + sig = SIGNAL (SIGINT, SIG_IGN); + flags = GETFLAGS( ttyb ); + SETFLAGS( ttyb, flags & ~ECHO ); + if (SETATTR(fileno(fi), &ttyb) < 0) + perror("SETATTR"); + } +#else + fi = stdin; +#endif + fprintf(stderr, "%s", prompt); + fflush(stderr); + i = 0; + while ( (c = getc(fi)) != EOF && c != '\n' && c != '\r' ) + if ( i < (sizeof(pbuf)-1) ) + pbuf[i++] = c; +#if defined(HAVE_TERMIOS_H) || defined(HAVE_SGTTY_H) + /* tidy up */ + if (fi != stdin) { + fprintf(stderr, "\n"); + fflush(stderr); + SETFLAGS( ttyb, flags ); + if (SETATTR(fileno(fi), &ttyb) < 0) + perror("SETATTR"); + (void) SIGNAL (SIGINT, sig); + (void) fclose(fi); + } +#endif + if ( c == EOF ) + return( NULL ); + pbuf[i] = '\0'; + return (pbuf); +} + +#endif /* !NEED_GETPASSPHRASE */ diff --git a/libraries/liblutil/getpeereid.c b/libraries/liblutil/getpeereid.c new file mode 100644 index 0000000..423fc7e --- /dev/null +++ b/libraries/liblutil/getpeereid.c @@ -0,0 +1,220 @@ +/* getpeereid.c */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2022 The OpenLDAP Foundation. + * 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 + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 /* Needed for glibc struct ucred */ +#endif + +#include "portable.h" + +#ifndef HAVE_GETPEEREID + +#include <sys/types.h> +#include <ac/unistd.h> + +#include <ac/socket.h> +#include <ac/errno.h> + +#ifdef HAVE_GETPEERUCRED +#include <ucred.h> +#endif + +#ifdef LDAP_PF_LOCAL_SENDMSG +#include <lber.h> +#ifdef HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif +#include <sys/stat.h> +#endif + +#ifdef HAVE_SYS_UCRED_H +#ifdef HAVE_GRP_H +#include <grp.h> /* for NGROUPS on Tru64 5.1 */ +#endif +#include <sys/ucred.h> +#endif + +#include <stdlib.h> + +int lutil_getpeereid( int s, uid_t *euid, gid_t *egid +#ifdef LDAP_PF_LOCAL_SENDMSG + , struct berval *peerbv +#endif + ) +{ +#ifdef LDAP_PF_LOCAL +#if defined( HAVE_GETPEERUCRED ) + ucred_t *uc = NULL; + if( getpeerucred( s, &uc ) == 0 ) { + *euid = ucred_geteuid( uc ); + *egid = ucred_getegid( uc ); + ucred_free( uc ); + return 0; + } + +#elif defined( SO_PEERCRED ) + struct ucred peercred; + ber_socklen_t peercredlen = sizeof peercred; + + if(( getsockopt( s, SOL_SOCKET, SO_PEERCRED, + (void *)&peercred, &peercredlen ) == 0 ) + && ( peercredlen == sizeof peercred )) + { + *euid = peercred.uid; + *egid = peercred.gid; + return 0; + } + +#elif defined( LOCAL_PEERCRED ) + struct xucred peercred; + ber_socklen_t peercredlen = sizeof peercred; + + if(( getsockopt( s, LOCAL_PEERCRED, 1, + (void *)&peercred, &peercredlen ) == 0 ) + && ( peercred.cr_version == XUCRED_VERSION )) + { + *euid = peercred.cr_uid; + *egid = peercred.cr_gid; + return 0; + } +#elif defined( LDAP_PF_LOCAL_SENDMSG ) && defined( MSG_WAITALL ) + int err, fd; + struct iovec iov; + struct msghdr msg = {0}; +# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL +# ifndef CMSG_SPACE +# define CMSG_SPACE(len) (_CMSG_ALIGN(sizeof(struct cmsghdr)) + _CMSG_ALIGN(len)) +# endif +# ifndef CMSG_LEN +# define CMSG_LEN(len) (_CMSG_ALIGN(sizeof(struct cmsghdr)) + (len)) +# endif + struct { + struct cmsghdr cm; + int fd; + } control_st; + struct cmsghdr *cmsg; +# endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */ + struct stat st; + struct sockaddr_un lname, rname; + ber_socklen_t llen, rlen; + + rlen = sizeof(rname); + llen = sizeof(lname); + memset( &lname, 0, sizeof( lname )); + getsockname(s, (struct sockaddr *)&lname, &llen); + + iov.iov_base = peerbv->bv_val; + iov.iov_len = peerbv->bv_len; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + peerbv->bv_len = 0; + +# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL + msg.msg_control = &control_st; + msg.msg_controllen = sizeof( struct cmsghdr ) + sizeof( int ); /* no padding! */ + + cmsg = CMSG_FIRSTHDR( &msg ); +# else + msg.msg_accrights = (char *)&fd; + msg.msg_accrightslen = sizeof(fd); +# endif + + /* + * AIX returns a bogus file descriptor if recvmsg() is + * called with MSG_PEEK (is this a bug?). Hence we need + * to receive the Abandon PDU. + */ + err = recvmsg( s, &msg, MSG_WAITALL ); + if( err >= 0 && +# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL + cmsg->cmsg_len == CMSG_LEN( sizeof(int) ) && + cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS +# else + msg.msg_accrightslen == sizeof(int) +# endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL*/ + ) { + int mode = S_IFIFO|S_ISUID|S_IRWXU; + + /* We must receive a valid descriptor, it must be a pipe, + * it must only be accessible by its owner, and it must + * have the name of our socket written on it. + */ + peerbv->bv_len = err; +# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL + fd = (*(int *)CMSG_DATA( cmsg )); +# endif + err = fstat( fd, &st ); + if ( err == 0 ) + rlen = read(fd, &rname, rlen); + close(fd); + if( err == 0 && st.st_mode == mode && + llen == rlen && !memcmp(&lname, &rname, llen)) + { + *euid = st.st_uid; + *egid = st.st_gid; + return 0; + } + } +#elif defined(SOCKCREDSIZE) + struct msghdr msg; + ber_socklen_t crmsgsize; + void *crmsg; + struct cmsghdr *cmp; + struct sockcred *sc; + + memset(&msg, 0, sizeof msg); + crmsgsize = CMSG_SPACE(SOCKCREDSIZE(NGROUPS)); + if (crmsgsize == 0) goto sc_err; + crmsg = malloc(crmsgsize); + if (crmsg == NULL) goto sc_err; + memset(crmsg, 0, crmsgsize); + + msg.msg_control = crmsg; + msg.msg_controllen = crmsgsize; + + if (recvmsg(s, &msg, 0) < 0) { + free(crmsg); + goto sc_err; + } + + if (msg.msg_controllen == 0 || (msg.msg_flags & MSG_CTRUNC) != 0) { + free(crmsg); + goto sc_err; + } + + cmp = CMSG_FIRSTHDR(&msg); + if (cmp->cmsg_level != SOL_SOCKET || cmp->cmsg_type != SCM_CREDS) { + printf("nocreds\n"); + goto sc_err; + } + + sc = (struct sockcred *)(void *)CMSG_DATA(cmp); + + *euid = sc->sc_euid; + *egid = sc->sc_egid; + + free(crmsg); + return 0; + +sc_err: +#endif +#endif /* LDAP_PF_LOCAL */ + + return -1; +} + +#endif /* HAVE_GETPEEREID */ diff --git a/libraries/liblutil/hash.c b/libraries/liblutil/hash.c new file mode 100644 index 0000000..10e56f0 --- /dev/null +++ b/libraries/liblutil/hash.c @@ -0,0 +1,141 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2022 The OpenLDAP Foundation. + * Portions Copyright 2000-2003 Kurt D. Zeilenga. + * 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 + * <http://www.OpenLDAP.org/license.html>. + */ + +/* This implements the Fowler / Noll / Vo (FNV-1) hash algorithm. + * A summary of the algorithm can be found at: + * http://www.isthe.com/chongo/tech/comp/fnv/index.html + */ + +#include "portable.h" + +#include <lutil_hash.h> + +/* offset and prime for 32-bit FNV-1 */ +#define HASH_OFFSET 0x811c9dc5U +#define HASH_PRIME 16777619 + + +/* + * Initialize context + */ +void +lutil_HASHInit( lutil_HASH_CTX *ctx ) +{ + ctx->hash = HASH_OFFSET; +} + +/* + * Update hash + */ +void +lutil_HASHUpdate( + lutil_HASH_CTX *ctx, + const unsigned char *buf, + ber_len_t len ) +{ + const unsigned char *p, *e; + ber_uint_t h; + + p = buf; + e = &buf[len]; + + h = ctx->hash; + + while( p < e ) { + h *= HASH_PRIME; + h ^= *p++; + } + + ctx->hash = h; +} + +/* + * Save hash + */ +void +lutil_HASHFinal( unsigned char *digest, lutil_HASH_CTX *ctx ) +{ + ber_uint_t h = ctx->hash; + + digest[0] = h & 0xffU; + digest[1] = (h>>8) & 0xffU; + digest[2] = (h>>16) & 0xffU; + digest[3] = (h>>24) & 0xffU; +} + +#ifdef HAVE_LONG_LONG + +/* 64 bit Fowler/Noll/Vo-O FNV-1a hash code */ + +#define HASH64_OFFSET 0xcbf29ce484222325ULL + +/* + * Initialize context + */ +void +lutil_HASH64Init( lutil_HASH_CTX *ctx ) +{ + ctx->hash64 = HASH64_OFFSET; +} + +/* + * Update hash + */ +void +lutil_HASH64Update( + lutil_HASH_CTX *ctx, + const unsigned char *buf, + ber_len_t len ) +{ + const unsigned char *p, *e; + unsigned long long h; + + p = buf; + e = &buf[len]; + + h = ctx->hash64; + + while( p < e ) { + /* xor the bottom with the current octet */ + h ^= *p++; + + /* multiply by the 64 bit FNV magic prime mod 2^64 */ + h += (h << 1) + (h << 4) + (h << 5) + + (h << 7) + (h << 8) + (h << 40); + + } + + ctx->hash64 = h; +} + +/* + * Save hash + */ +void +lutil_HASH64Final( unsigned char *digest, lutil_HASH_CTX *ctx ) +{ + unsigned long long h = ctx->hash64; + + digest[0] = h & 0xffU; + digest[1] = (h>>8) & 0xffU; + digest[2] = (h>>16) & 0xffU; + digest[3] = (h>>24) & 0xffU; + digest[4] = (h>>32) & 0xffU; + digest[5] = (h>>40) & 0xffU; + digest[6] = (h>>48) & 0xffU; + digest[7] = (h>>56) & 0xffU; +} +#endif /* HAVE_LONG_LONG */ diff --git a/libraries/liblutil/lockf.c b/libraries/liblutil/lockf.c new file mode 100644 index 0000000..e939909 --- /dev/null +++ b/libraries/liblutil/lockf.c @@ -0,0 +1,118 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * 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 + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * File Locking Routines + * + * Implementations (in order of preference) + * - lockf + * - fcntl + * - flock + * + * Other implementations will be added as needed. + * + * NOTE: lutil_lockf() MUST block until an exclusive lock is acquired. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/unistd.h> + +#undef LOCK_API + +#if defined(HAVE_LOCKF) && defined(F_LOCK) +# define USE_LOCKF 1 +# define LOCK_API "lockf" +#endif + +#if !defined(LOCK_API) && defined(HAVE_FCNTL) +# ifdef HAVE_FCNTL_H +# include <fcntl.h> +# endif +# ifdef F_WRLCK +# define USE_FCNTL 1 +# define LOCK_API "fcntl" +# endif +#endif + +#if !defined(LOCK_API) && defined(HAVE_FLOCK) +# ifdef HAVE_SYS_FILE_H +# include <sys/file.h> +# endif +# define USE_FLOCK 1 +# define LOCK_API "flock" +#endif + +#if !defined(USE_LOCKF) && !defined(USE_FCNTL) && !defined(USE_FLOCK) +int lutil_lockf ( int fd ) { + fd = fd; + return 0; +} + +int lutil_unlockf ( int fd ) { + fd = fd; + return 0; +} +#endif + +#ifdef USE_LOCKF +int lutil_lockf ( int fd ) { + /* use F_LOCK instead of F_TLOCK, ie: block */ + return lockf( fd, F_LOCK, 0 ); +} + +int lutil_unlockf ( int fd ) { + return lockf( fd, F_ULOCK, 0 ); +} +#endif + +#ifdef USE_FCNTL +int lutil_lockf ( int fd ) { + struct flock file_lock; + + memset( &file_lock, '\0', sizeof( file_lock ) ); + file_lock.l_type = F_WRLCK; + file_lock.l_whence = SEEK_SET; + file_lock.l_start = 0; + file_lock.l_len = 0; + + /* use F_SETLKW instead of F_SETLK, ie: block */ + return( fcntl( fd, F_SETLKW, &file_lock ) ); +} + +int lutil_unlockf ( int fd ) { + struct flock file_lock; + + memset( &file_lock, '\0', sizeof( file_lock ) ); + file_lock.l_type = F_UNLCK; + file_lock.l_whence = SEEK_SET; + file_lock.l_start = 0; + file_lock.l_len = 0; + + return( fcntl ( fd, F_SETLKW, &file_lock ) ); +} +#endif + +#ifdef USE_FLOCK +int lutil_lockf ( int fd ) { + /* use LOCK_EX instead of LOCK_EX|LOCK_NB, ie: block */ + return flock( fd, LOCK_EX ); +} + +int lutil_unlockf ( int fd ) { + return flock( fd, LOCK_UN ); +} +#endif diff --git a/libraries/liblutil/md5.c b/libraries/liblutil/md5.c new file mode 100644 index 0000000..c895cb7 --- /dev/null +++ b/libraries/liblutil/md5.c @@ -0,0 +1,332 @@ +/* md5.c -- MD5 message-digest algorithm */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * 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 + * <http://www.OpenLDAP.org/license.html>. + */ +/* This work was adapted for inclusion in OpenLDAP Software by + * Kurt D. Zeilenga based upon code developed by Colin Plumb + * and subsequently modified by Jim Kingdon. + */ + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +/* This code was modified in 1997 by Jim Kingdon of Cyclic Software to + not require an integer type which is exactly 32 bits. This work + draws on the changes for the same purpose by Tatu Ylonen + <ylo@cs.hut.fi> as part of SSH, but since I didn't actually use + that code, there is no copyright issue. I hereby disclaim + copyright in any changes I have made; this code remains in the + public domain. */ + +#include "portable.h" + +#include <ac/string.h> + +/* include socket.h to get sys/types.h and/or winsock2.h */ +#include <ac/socket.h> + +#include <lutil_md5.h> + +/* Little-endian byte-swapping routines. Note that these do not + depend on the size of datatypes such as ber_uint_t, nor do they require + us to detect the endianness of the machine we are running on. It + is possible they should be macros for speed, but I would be + surprised if they were a performance bottleneck for MD5. */ + +static ber_uint_t +getu32( const unsigned char *addr ) +{ + return (((((unsigned long)addr[3] << 8) | addr[2]) << 8) + | addr[1]) << 8 | addr[0]; +} + +static void +putu32( ber_uint_t data, unsigned char *addr ) +{ + addr[0] = (unsigned char)data; + addr[1] = (unsigned char)(data >> 8); + addr[2] = (unsigned char)(data >> 16); + addr[3] = (unsigned char)(data >> 24); +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void +lutil_MD5Init( struct lutil_MD5Context *ctx ) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +lutil_MD5Update( + struct lutil_MD5Context *ctx, + const unsigned char *buf, + ber_len_t len +) +{ + ber_uint_t t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = (t + ((ber_uint_t)len << 3)) & 0xffffffff) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if ( t ) { + unsigned char *p = ctx->in + t; + + t = 64-t; + if (len < t) { + AC_MEMCPY(p, buf, len); + return; + } + AC_MEMCPY(p, buf, t); + lutil_MD5Transform(ctx->buf, ctx->in); + buf += t; + len -= t; + } + + /* Process data in 64-byte chunks */ + + while (len >= 64) { + AC_MEMCPY(ctx->in, buf, 64); + lutil_MD5Transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + AC_MEMCPY(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void +lutil_MD5Final( unsigned char *digest, struct lutil_MD5Context *ctx ) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, '\0', count); + lutil_MD5Transform(ctx->buf, ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, '\0', 56); + } else { + /* Pad block to 56 bytes */ + memset(p, '\0', count-8); + } + + /* Append length in bits and transform */ + putu32(ctx->bits[0], ctx->in + 56); + putu32(ctx->bits[1], ctx->in + 60); + + lutil_MD5Transform(ctx->buf, ctx->in); + putu32(ctx->buf[0], digest); + putu32(ctx->buf[1], digest + 4); + putu32(ctx->buf[2], digest + 8); + putu32(ctx->buf[3], digest + 12); + memset(ctx, '\0', sizeof(*ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w &= 0xffffffff, w = w<<s | w>>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void +lutil_MD5Transform( ber_uint_t *buf, const unsigned char *inraw ) +{ + register ber_uint_t a, b, c, d; + ber_uint_t in[16]; + int i; + + for (i = 0; i < 16; ++i) + in[i] = getu32 (inraw + 4 * i); + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} +#endif + +#ifdef TEST +/* Simple test program. Can use it to manually run the tests from + RFC1321 for example. */ +#include <stdio.h> + +int +main (int argc, char **argv ) +{ + struct lutil_MD5Context context; + unsigned char checksum[LUTIL_MD5_BYTES]; + int i; + int j; + + if (argc < 2) + { + fprintf (stderr, "usage: %s string-to-hash\n", argv[0]); + return EXIT_FAILURE; + } + for (j = 1; j < argc; ++j) + { + printf ("MD5 (\"%s\") = ", argv[j]); + lutil_MD5Init (&context); + lutil_MD5Update (&context, argv[j], strlen (argv[j])); + lutil_MD5Final (checksum, &context); + for (i = 0; i < LUTIL_MD5_BYTES; i++) + { + printf ("%02x", (unsigned int) checksum[i]); + } + printf ("\n"); + } + return EXIT_SUCCESS; +} +#endif /* TEST */ diff --git a/libraries/liblutil/memcmp.c b/libraries/liblutil/memcmp.c new file mode 100644 index 0000000..8068de3 --- /dev/null +++ b/libraries/liblutil/memcmp.c @@ -0,0 +1,33 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * 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 + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <ac/string.h> + +/* + * Memory Compare + */ +int +(lutil_memcmp)(const void *v1, const void *v2, size_t n) +{ + if (n != 0) { + const unsigned char *s1=v1, *s2=v2; + do { + if (*s1++ != *s2++) return *--s1 - *--s2; + } while (--n != 0); + } + return 0; +} diff --git a/libraries/liblutil/meter.c b/libraries/liblutil/meter.c new file mode 100644 index 0000000..8ac592f --- /dev/null +++ b/libraries/liblutil/meter.c @@ -0,0 +1,386 @@ +/* meter.c - lutil_meter meters */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright (c) 2009 by Emily Backes, 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 + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Emily Backes for inclusion + * in OpenLDAP software. + */ + +#include "portable.h" +#include "lutil_meter.h" + +#include <ac/assert.h> +#include <ac/string.h> + +int +lutil_time_string ( + char *dest, + int duration, + int max_terms) +{ + static const int time_div[] = {31556952, + 604800, + 86400, + 3600, + 60, + 1, + 0}; + const int * time_divp = time_div; + static const char * time_name_ch = "ywdhms"; + const char * time_name_chp = time_name_ch; + int term_count = 0; + char *buf = dest; + int time_quot; + + assert ( max_terms >= 2 ); /* room for "none" message */ + + if ( duration < 0 ) { + *dest = '\0'; + return 1; + } + if ( duration == 0 ) { + strcpy( dest, "none" ); + return 0; + } + while ( term_count < max_terms && duration > 0 ) { + if (duration > *time_divp) { + time_quot = duration / *time_divp; + duration %= *time_divp; + if (time_quot > 99) { + return 1; + } else { + *(buf++) = time_quot / 10 + '0'; + *(buf++) = time_quot % 10 + '0'; + *(buf++) = *time_name_chp; + ++term_count; + } + } + if ( *(++time_divp) == 0) duration = 0; + ++time_name_chp; + } + *buf = '\0'; + return 0; +} + +int +lutil_get_now (double *now) +{ +#ifdef HAVE_GETTIMEOFDAY + struct timeval tv; + + assert( now ); + gettimeofday( &tv, NULL ); + *now = ((double) tv.tv_sec) + (((double) tv.tv_usec) / 1000000.0); + return 0; +#else + time_t tm; + + assert( now ); + time( &tm ); + *now = (double) tm; + return 0; +#endif +} + +int +lutil_meter_open ( + lutil_meter_t *meter, + const lutil_meter_display_t *display, + const lutil_meter_estimator_t *estimator, + size_t goal_value) +{ + int rc; + + assert( meter != NULL ); + assert( display != NULL ); + assert( estimator != NULL ); + + if (goal_value < 1) return -1; + + memset( (void*) meter, 0, sizeof( lutil_meter_t )); + meter->display = display; + meter->estimator = estimator; + lutil_get_now( &meter->start_time ); + meter->last_update = meter->start_time; + meter->goal_value = goal_value; + meter->last_position = 0; + + rc = meter->display->display_open( &meter->display_data ); + if( rc != 0 ) return rc; + + rc = meter->estimator->estimator_open( &meter->estimator_data ); + if( rc != 0 ) { + meter->display->display_close( &meter->display_data ); + return rc; + } + + return 0; +} + +int +lutil_meter_update ( + lutil_meter_t *meter, + size_t position, + int force) +{ + static const double display_rate = 0.5; + double frac, cycle_length, speed, now; + time_t remaining_time, elapsed; + int rc; + + assert( meter != NULL ); + + lutil_get_now( &now ); + + if ( !force && now - meter->last_update < display_rate ) return 0; + + frac = ((double)position) / ((double) meter->goal_value); + elapsed = now - meter->start_time; + if (frac <= 0.0 || elapsed == 0) return 0; + if (frac >= 1.0) { + rc = meter->display->display_update( + &meter->display_data, + 1.0, + 0, + (time_t) elapsed, + ((double)position) / elapsed); + } else { + rc = meter->estimator->estimator_update( + &meter->estimator_data, + meter->start_time, + frac, + &remaining_time ); + if ( rc == 0 ) { + cycle_length = now - meter->last_update; + speed = cycle_length > 0.0 ? + ((double)(position - meter->last_position)) + / cycle_length : + 0.0; + rc = meter->display->display_update( + &meter->display_data, + frac, + remaining_time, + (time_t) elapsed, + speed); + if ( rc == 0 ) { + meter->last_update = now; + meter->last_position = position; + } + } + } + + return rc; +} + +int +lutil_meter_close (lutil_meter_t *meter) +{ + meter->estimator->estimator_close( &meter->estimator_data ); + meter->display->display_close( &meter->display_data ); + + return 0; +} + +/* Default display and estimator */ +typedef struct { + int buffer_length; + char * buffer; + int need_eol; + int phase; + FILE *output; +} text_display_state_t; + +static int +text_open (void ** display_datap) +{ + static const int default_buffer_length = 81; + text_display_state_t *data; + + assert( display_datap != NULL ); + data = calloc( 1, sizeof( text_display_state_t )); + assert( data != NULL ); + data->buffer_length = default_buffer_length; + data->buffer = calloc( 1, default_buffer_length ); + assert( data->buffer != NULL ); + data->output = stderr; + *display_datap = data; + return 0; +} + +static int +text_update ( + void **display_datap, + double frac, + time_t remaining_time, + time_t elapsed, + double byte_rate) +{ + text_display_state_t *data; + char *buf, *buf_end; + + assert( display_datap != NULL ); + assert( *display_datap != NULL ); + data = (text_display_state_t*) *display_datap; + + if ( data->output == NULL ) return 1; + + buf = data->buffer; + buf_end = buf + data->buffer_length - 1; + +/* |#################### 100.00% eta 1d19h elapsed 23w 7d23h15m12s spd nnnn.n M/s */ + + { + /* spinner */ + static const int phase_mod = 8; + static const char phase_char[] = "_.-*\"*-."; + *buf++ = phase_char[data->phase % phase_mod]; + data->phase++; + } + + { + /* bar */ + static const int bar_length = 20; + static const double bar_lengthd = 20.0; + static const char fill_char = '#'; + static const char blank_char = ' '; + char *bar_end = buf + bar_length; + char *bar_pos = frac < 0.0 ? + buf : + frac < 1.0 ? + buf + (int) (bar_lengthd * frac) : + bar_end; + + assert( (buf_end - buf) > bar_length ); + while ( buf < bar_end ) { + *buf = buf < bar_pos ? + fill_char : blank_char; + ++buf; + } + } + + { + /* percent */ + (void) snprintf( buf, buf_end-buf, "%7.2f%%", 100.0*frac ); + buf += 8; + } + + { + /* eta and elapsed */ + char time_buffer[19]; + int rc; + rc = lutil_time_string( time_buffer, remaining_time, 2); + if (rc == 0) + snprintf( buf, buf_end-buf, " eta %6s", time_buffer ); + buf += 5+6; + rc = lutil_time_string( time_buffer, elapsed, 5); + if (rc == 0) + snprintf( buf, buf_end-buf, " elapsed %15s", + time_buffer ); + buf += 9+15; + } + + { + /* speed */ + static const char prefixes[] = " kMGTPEZY"; + const char *prefix_chp = prefixes; + + while (*prefix_chp && byte_rate >= 1024.0) { + byte_rate /= 1024.0; + ++prefix_chp; + } + if ( byte_rate >= 1024.0 ) { + snprintf( buf, buf_end-buf, " fast!" ); + buf += 6; + } else { + snprintf( buf, buf_end-buf, " spd %5.1f %c/s", + byte_rate, + *prefix_chp); + buf += 5+6+4; + } + } + + (void) fprintf( data->output, + "\r%-79s", + data->buffer ); + data->need_eol = 1; + return 0; +} + +static int +text_close (void ** display_datap) +{ + text_display_state_t *data; + + if (display_datap) { + if (*display_datap) { + data = (text_display_state_t*) *display_datap; + if (data->output && data->need_eol) + fputs ("\n", data->output); + if (data->buffer) + free( data->buffer ); + free( data ); + } + *display_datap = NULL; + } + return 0; +} + +static int +null_open_close (void **datap) +{ + assert( datap ); + *datap = NULL; + return 0; +} + +static int +linear_update ( + void **estimator_datap, + double start, + double frac, + time_t *remaining) +{ + double now; + double elapsed; + + assert( estimator_datap != NULL ); + assert( *estimator_datap == NULL ); + assert( start > 0.0 ); + assert( frac >= 0.0 ); + assert( frac <= 1.0 ); + assert( remaining != NULL ); + lutil_get_now( &now ); + + elapsed = now-start; + assert( elapsed >= 0.0 ); + + if ( frac == 0.0 ) { + return 1; + } else if ( frac >= 1.0 ) { + *remaining = 0; + return 0; + } else { + *remaining = (time_t) (elapsed/frac-elapsed+0.5); + return 0; + } +} + +const lutil_meter_display_t lutil_meter_text_display = { + text_open, text_update, text_close +}; + +const lutil_meter_estimator_t lutil_meter_linear_estimator = { + null_open_close, linear_update, null_open_close +}; diff --git a/libraries/liblutil/ntservice.c b/libraries/liblutil/ntservice.c new file mode 100644 index 0000000..debc1c3 --- /dev/null +++ b/libraries/liblutil/ntservice.c @@ -0,0 +1,509 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * 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 + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * NT Service manager utilities for OpenLDAP services + */ + +#include "portable.h" + +#ifdef HAVE_NT_SERVICE_MANAGER + +#include <ac/stdlib.h> +#include <ac/string.h> + +#include <stdio.h> + +#include <windows.h> +#include <winsvc.h> + +#include <ldap.h> + +#include "ldap_pvt_thread.h" + +#include "ldap_defaults.h" + +#include "slapdmsg.h" + +#define SCM_NOTIFICATION_INTERVAL 5000 +#define THIRTY_SECONDS (30 * 1000) + +int is_NT_Service; /* is this is an NT service? */ + +SERVICE_STATUS lutil_ServiceStatus; +SERVICE_STATUS_HANDLE hlutil_ServiceStatus; + +ldap_pvt_thread_cond_t started_event, stopped_event; +ldap_pvt_thread_t start_status_tid, stop_status_tid; + +void (*stopfunc)(int); + +static char *GetLastErrorString( void ); + +int lutil_srv_install(LPCTSTR lpszServiceName, LPCTSTR lpszDisplayName, + LPCTSTR lpszBinaryPathName, int auto_start) +{ + HKEY hKey; + DWORD dwValue, dwDisposition; + SC_HANDLE schSCManager, schService; + char *sp = strrchr( lpszBinaryPathName, '\\'); + + if ( sp ) sp = strchr(sp, ' '); + if ( sp ) *sp = '\0'; + fprintf( stderr, "The install path is %s.\n", lpszBinaryPathName ); + if ( sp ) *sp = ' '; + if ((schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT|SC_MANAGER_CREATE_SERVICE ) ) != NULL ) + { + if ((schService = CreateService( + schSCManager, + lpszServiceName, + lpszDisplayName, + SERVICE_ALL_ACCESS, + SERVICE_WIN32_OWN_PROCESS, + auto_start ? SERVICE_AUTO_START : SERVICE_DEMAND_START, + SERVICE_ERROR_NORMAL, + lpszBinaryPathName, + NULL, NULL, NULL, NULL, NULL)) != NULL) + { + char regpath[132]; + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); + + snprintf( regpath, sizeof regpath, + "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s", + lpszServiceName ); + /* Create the registry key for event logging to the Windows NT event log. */ + if ( RegCreateKeyEx(HKEY_LOCAL_MACHINE, + regpath, 0, + "REG_SZ", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, + &dwDisposition) != ERROR_SUCCESS) + { + fprintf( stderr, "RegCreateKeyEx() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); + RegCloseKey(hKey); + return(0); + } + if ( sp ) *sp = '\0'; + if ( RegSetValueEx(hKey, "EventMessageFile", 0, REG_EXPAND_SZ, lpszBinaryPathName, strlen(lpszBinaryPathName) + 1) != ERROR_SUCCESS) + { + fprintf( stderr, "RegSetValueEx(EventMessageFile) failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); + RegCloseKey(hKey); + return(0); + } + + dwValue = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE; + if ( RegSetValueEx(hKey, "TypesSupported", 0, REG_DWORD, (LPBYTE) &dwValue, sizeof(DWORD)) != ERROR_SUCCESS) + { + fprintf( stderr, "RegCreateKeyEx(TypesSupported) failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); + RegCloseKey(hKey); + return(0); + } + RegCloseKey(hKey); + return(1); + } + else + { + fprintf( stderr, "CreateService() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); + CloseServiceHandle(schSCManager); + return(0); + } + } + else + fprintf( stderr, "OpenSCManager() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); + return(0); +} + + +int lutil_srv_remove(LPCTSTR lpszServiceName, LPCTSTR lpszBinaryPathName) +{ + SC_HANDLE schSCManager, schService; + + fprintf( stderr, "The installed path is %s.\n", lpszBinaryPathName ); + if ((schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT|SC_MANAGER_CREATE_SERVICE)) != NULL ) + { + if ((schService = OpenService(schSCManager, lpszServiceName, DELETE)) != NULL) + { + if ( DeleteService(schService) == TRUE) + { + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); + return(1); + } else { + fprintf( stderr, "DeleteService() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); + fprintf( stderr, "The %s service has not been removed.\n", lpszBinaryPathName); + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); + return(0); + } + } else { + fprintf( stderr, "OpenService() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); + CloseServiceHandle(schSCManager); + return(0); + } + } + else + fprintf( stderr, "OpenSCManager() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); + return(0); +} + + +#if 0 /* unused */ +DWORD +svc_installed (LPTSTR lpszServiceName, LPTSTR lpszBinaryPathName) +{ + char buf[256]; + HKEY key; + DWORD rc; + DWORD type; + long len; + + strcpy(buf, TEXT("SYSTEM\\CurrentControlSet\\Services\\")); + strcat(buf, lpszServiceName); + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, buf, 0, KEY_QUERY_VALUE, &key) != ERROR_SUCCESS) + return(-1); + + rc = 0; + if (lpszBinaryPathName) { + len = sizeof(buf); + if (RegQueryValueEx(key, "ImagePath", NULL, &type, buf, &len) == ERROR_SUCCESS) { + if (strcmp(lpszBinaryPathName, buf)) + rc = -1; + } + } + RegCloseKey(key); + return(rc); +} + + +DWORD +svc_running (LPTSTR lpszServiceName) +{ + SC_HANDLE service; + SC_HANDLE scm; + DWORD rc; + SERVICE_STATUS ss; + + if (!(scm = OpenSCManager(NULL, NULL, GENERIC_READ))) + return(GetLastError()); + + rc = 1; + service = OpenService(scm, lpszServiceName, SERVICE_QUERY_STATUS); + if (service) { + if (!QueryServiceStatus(service, &ss)) + rc = GetLastError(); + else if (ss.dwCurrentState != SERVICE_STOPPED) + rc = 0; + CloseServiceHandle(service); + } + CloseServiceHandle(scm); + return(rc); +} +#endif + +static void *start_status_routine( void *ptr ) +{ + DWORD wait_result; + int done = 0; + + while ( !done ) + { + wait_result = WaitForSingleObject( started_event, SCM_NOTIFICATION_INTERVAL ); + switch ( wait_result ) + { + case WAIT_ABANDONED: + case WAIT_OBJECT_0: + /* the object that we were waiting for has been destroyed (ABANDONED) or + * signalled (TIMEOUT_0). We can assume that the startup process is + * complete and tell the Service Control Manager that we are now runnng */ + lutil_ServiceStatus.dwCurrentState = SERVICE_RUNNING; + lutil_ServiceStatus.dwWin32ExitCode = NO_ERROR; + lutil_ServiceStatus.dwCheckPoint++; + lutil_ServiceStatus.dwWaitHint = 1000; + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + done = 1; + break; + case WAIT_TIMEOUT: + /* We've waited for the required time, so send an update to the Service Control + * Manager saying to wait again. */ + lutil_ServiceStatus.dwCheckPoint++; + lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2; + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + break; + case WAIT_FAILED: + /* there's been some problem with WaitForSingleObject so tell the Service + * Control Manager to wait 30 seconds before deploying its assassin and + * then leave the thread. */ + lutil_ServiceStatus.dwCheckPoint++; + lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS; + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + done = 1; + break; + } + } + ldap_pvt_thread_exit(NULL); + return NULL; +} + + + +static void *stop_status_routine( void *ptr ) +{ + DWORD wait_result; + int done = 0; + + while ( !done ) + { + wait_result = WaitForSingleObject( stopped_event, SCM_NOTIFICATION_INTERVAL ); + switch ( wait_result ) + { + case WAIT_ABANDONED: + case WAIT_OBJECT_0: + /* the object that we were waiting for has been destroyed (ABANDONED) or + * signalled (TIMEOUT_0). The shutting down process is therefore complete + * and the final SERVICE_STOPPED message will be sent to the service control + * manager prior to the process terminating. */ + done = 1; + break; + case WAIT_TIMEOUT: + /* We've waited for the required time, so send an update to the Service Control + * Manager saying to wait again. */ + lutil_ServiceStatus.dwCheckPoint++; + lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2; + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + break; + case WAIT_FAILED: + /* there's been some problem with WaitForSingleObject so tell the Service + * Control Manager to wait 30 seconds before deploying its assassin and + * then leave the thread. */ + lutil_ServiceStatus.dwCheckPoint++; + lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS; + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + done = 1; + break; + } + } + ldap_pvt_thread_exit(NULL); + return NULL; +} + + + +static void WINAPI lutil_ServiceCtrlHandler( IN DWORD Opcode) +{ + switch (Opcode) + { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + + lutil_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; + lutil_ServiceStatus.dwCheckPoint++; + lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2; + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + + ldap_pvt_thread_cond_init( &stopped_event ); + if ( stopped_event == NULL ) + { + /* the event was not created. We will ask the service control manager for 30 + * seconds to shutdown */ + lutil_ServiceStatus.dwCheckPoint++; + lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS; + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + } + else + { + /* start a thread to report the progress to the service control manager + * until the stopped_event is fired. */ + if ( ldap_pvt_thread_create( &stop_status_tid, 0, stop_status_routine, NULL ) == 0 ) + { + + } + else { + /* failed to create the thread that tells the Service Control Manager that the + * service stopping is proceeding. + * tell the Service Control Manager to wait another 30 seconds before deploying its + * assassin. */ + lutil_ServiceStatus.dwCheckPoint++; + lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS; + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + } + } + stopfunc( -1 ); + break; + + case SERVICE_CONTROL_INTERROGATE: + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + break; + } + return; +} + +void *lutil_getRegParam( char *svc, char *value ) +{ + HKEY hkey; + char path[255]; + DWORD vType; + static char vValue[1024]; + DWORD valLen = sizeof( vValue ); + + if ( svc != NULL ) + snprintf ( path, sizeof path, "SOFTWARE\\%s", svc ); + else + snprintf ( path, sizeof path, "SOFTWARE\\OpenLDAP\\Parameters" ); + + if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, path, 0, KEY_READ, &hkey ) != ERROR_SUCCESS ) + { + return NULL; + } + + if ( RegQueryValueEx( hkey, value, NULL, &vType, vValue, &valLen ) != ERROR_SUCCESS ) + { + RegCloseKey( hkey ); + return NULL; + } + RegCloseKey( hkey ); + + switch ( vType ) + { + case REG_BINARY: + case REG_DWORD: + return (void*)&vValue; + case REG_SZ: + return (void*)&vValue; + } + return (void*)NULL; +} + +void lutil_LogStartedEvent( char *svc, int slap_debug, char *configfile, char *urls ) +{ + char *Inserts[5]; + WORD i = 0, j; + HANDLE hEventLog; + + hEventLog = RegisterEventSource( NULL, svc ); + + Inserts[i] = (char *)malloc( 20 ); + itoa( slap_debug, Inserts[i++], 10 ); + Inserts[i++] = strdup( configfile ); + Inserts[i++] = strdup( urls ? urls : "ldap:///" ); + + ReportEvent( hEventLog, EVENTLOG_INFORMATION_TYPE, 0, + MSG_SVC_STARTED, NULL, i, 0, (LPCSTR *) Inserts, NULL ); + + for ( j = 0; j < i; j++ ) + ldap_memfree( Inserts[j] ); + DeregisterEventSource( hEventLog ); +} + + + +void lutil_LogStoppedEvent( char *svc ) +{ + HANDLE hEventLog; + + hEventLog = RegisterEventSource( NULL, svc ); + ReportEvent( hEventLog, EVENTLOG_INFORMATION_TYPE, 0, + MSG_SVC_STOPPED, NULL, 0, 0, NULL, NULL ); + DeregisterEventSource( hEventLog ); +} + + +void lutil_CommenceStartupProcessing( char *lpszServiceName, + void (*stopper)(int) ) +{ + hlutil_ServiceStatus = RegisterServiceCtrlHandler( lpszServiceName, (LPHANDLER_FUNCTION)lutil_ServiceCtrlHandler); + + stopfunc = stopper; + + /* initialize the Service Status structure */ + lutil_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + lutil_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; + lutil_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + lutil_ServiceStatus.dwWin32ExitCode = NO_ERROR; + lutil_ServiceStatus.dwServiceSpecificExitCode = 0; + lutil_ServiceStatus.dwCheckPoint = 1; + lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2; + + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + + /* start up a thread to keep sending SERVICE_START_PENDING to the Service Control Manager + * until the slapd listener is completed and listening. Only then should we send + * SERVICE_RUNNING to the Service Control Manager. */ + ldap_pvt_thread_cond_init( &started_event ); + if ( started_event == NULL) + { + /* failed to create the event to determine when the startup process is complete so + * tell the Service Control Manager to wait another 30 seconds before deploying its + * assassin */ + lutil_ServiceStatus.dwCheckPoint++; + lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS; + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + } + else + { + /* start a thread to report the progress to the service control manager + * until the started_event is fired. */ + if ( ldap_pvt_thread_create( &start_status_tid, 0, start_status_routine, NULL ) == 0 ) + { + + } + else { + /* failed to create the thread that tells the Service Control Manager that the + * service startup is proceeding. + * tell the Service Control Manager to wait another 30 seconds before deploying its + * assassin. */ + lutil_ServiceStatus.dwCheckPoint++; + lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS; + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + } + } +} + +void lutil_ReportShutdownComplete( ) +{ + if ( is_NT_Service ) + { + /* stop sending SERVICE_STOP_PENDING messages to the Service Control Manager */ + ldap_pvt_thread_cond_signal( &stopped_event ); + ldap_pvt_thread_cond_destroy( &stopped_event ); + + /* wait for the thread sending the SERVICE_STOP_PENDING messages to the Service Control Manager to die. + * if the wait fails then put ourselves to sleep for half the Service Control Manager update interval */ + if (ldap_pvt_thread_join( stop_status_tid, (void *) NULL ) == -1) + ldap_pvt_thread_sleep( SCM_NOTIFICATION_INTERVAL / 2 ); + + lutil_ServiceStatus.dwCurrentState = SERVICE_STOPPED; + lutil_ServiceStatus.dwCheckPoint++; + lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL; + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + } +} + +static char *GetErrorString( int err ) +{ + static char msgBuf[1024]; + + FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + msgBuf, 1024, NULL ); + + return msgBuf; +} + +static char *GetLastErrorString( void ) +{ + return GetErrorString( GetLastError() ); +} +#endif diff --git a/libraries/liblutil/passfile.c b/libraries/liblutil/passfile.c new file mode 100644 index 0000000..666b718 --- /dev/null +++ b/libraries/liblutil/passfile.c @@ -0,0 +1,110 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * 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 + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/ctype.h> +#include <ac/string.h> + +#ifdef HAVE_FSTAT +#include <sys/types.h> +#include <sys/stat.h> +#endif /* HAVE_FSTAT */ + +#include <lber.h> +#include <lutil.h> + +/* Get a password from a file. */ +int +lutil_get_filed_password( + const char *filename, + struct berval *passwd ) +{ + size_t nread, nleft, nr; + FILE *f = fopen( filename, "r" ); + + if( f == NULL ) { + perror( filename ); + return -1; + } + + passwd->bv_val = NULL; + passwd->bv_len = 4096; + +#ifdef HAVE_FSTAT + { + struct stat sb; + if ( fstat( fileno( f ), &sb ) == 0 ) { + if( sb.st_mode & 006 ) { + fprintf( stderr, _("Warning: Password file %s" + " is publicly readable/writeable\n"), + filename ); + } + + if ( sb.st_size ) + passwd->bv_len = sb.st_size; + } + } +#endif /* HAVE_FSTAT */ + + passwd->bv_val = (char *) ber_memalloc( passwd->bv_len + 1 ); + if( passwd->bv_val == NULL ) { + perror( filename ); + fclose( f ); + return -1; + } + + nread = 0; + nleft = passwd->bv_len; + do { + if( nleft == 0 ) { + /* double the buffer size */ + char *p = (char *) ber_memrealloc( passwd->bv_val, + 2 * passwd->bv_len + 1 ); + if( p == NULL ) { + ber_memfree( passwd->bv_val ); + passwd->bv_val = NULL; + passwd->bv_len = 0; + fclose( f ); + return -1; + } + nleft = passwd->bv_len; + passwd->bv_len *= 2; + passwd->bv_val = p; + } + + nr = fread( &passwd->bv_val[nread], 1, nleft, f ); + + if( nr < nleft && ferror( f ) ) { + ber_memfree( passwd->bv_val ); + passwd->bv_val = NULL; + passwd->bv_len = 0; + fclose( f ); + return -1; + } + + nread += nr; + nleft -= nr; + } while ( !feof(f) ); + + passwd->bv_len = nread; + passwd->bv_val[nread] = '\0'; + + fclose( f ); + return 0; +} diff --git a/libraries/liblutil/passwd.c b/libraries/liblutil/passwd.c new file mode 100644 index 0000000..653cb5a --- /dev/null +++ b/libraries/liblutil/passwd.c @@ -0,0 +1,935 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * 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 + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * int lutil_passwd( + * const struct berval *passwd, + * const struct berval *cred, + * const char **schemes ) + * + * Returns true if user supplied credentials (cred) matches + * the stored password (passwd). + * + * Due to the use of the crypt(3) function + * this routine is NOT thread-safe. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/unistd.h> +#include <ac/param.h> + +#ifdef SLAPD_CRYPT +# include <ac/crypt.h> + +# if defined( HAVE_GETPWNAM ) && defined( HAVE_STRUCT_PASSWD_PW_PASSWD ) +# ifdef HAVE_SHADOW_H +# include <shadow.h> +# endif +# ifdef HAVE_PWD_H +# include <pwd.h> +# endif +# ifdef HAVE_AIX_SECURITY +# include <userpw.h> +# endif +# endif +#endif + +#include <lber.h> + +#include "ldap_pvt.h" +#include "lber_pvt.h" + +#include "lutil_md5.h" +#include "lutil_sha1.h" +#include "lutil.h" + +static const unsigned char crypt64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890./"; + +#ifdef SLAPD_CRYPT +static char *salt_format = NULL; +static lutil_cryptfunc lutil_crypt; +lutil_cryptfunc *lutil_cryptptr = lutil_crypt; +#endif + +/* KLUDGE: + * chk_fn is NULL iff name is {CLEARTEXT} + * otherwise, things will break + */ +struct pw_scheme { + struct berval name; + LUTIL_PASSWD_CHK_FUNC *chk_fn; + LUTIL_PASSWD_HASH_FUNC *hash_fn; +}; + +struct pw_slist { + struct pw_slist *next; + struct pw_scheme s; +}; + +/* password check routines */ + +#define SALT_SIZE 4 + +static LUTIL_PASSWD_CHK_FUNC chk_md5; +static LUTIL_PASSWD_CHK_FUNC chk_smd5; +static LUTIL_PASSWD_HASH_FUNC hash_smd5; +static LUTIL_PASSWD_HASH_FUNC hash_md5; + + +#ifdef LUTIL_SHA1_BYTES +static LUTIL_PASSWD_CHK_FUNC chk_ssha1; +static LUTIL_PASSWD_CHK_FUNC chk_sha1; +static LUTIL_PASSWD_HASH_FUNC hash_sha1; +static LUTIL_PASSWD_HASH_FUNC hash_ssha1; +#endif + + +#ifdef SLAPD_CRYPT +static LUTIL_PASSWD_CHK_FUNC chk_crypt; +static LUTIL_PASSWD_HASH_FUNC hash_crypt; + +#if defined( HAVE_GETPWNAM ) && defined( HAVE_STRUCT_PASSWD_PW_PASSWD ) +static LUTIL_PASSWD_CHK_FUNC chk_unix; +#endif +#endif + +/* password hash routines */ + +#ifdef SLAPD_CLEARTEXT +static LUTIL_PASSWD_HASH_FUNC hash_clear; +#endif + +static struct pw_slist *pw_schemes; +static int pw_inited; + +static const struct pw_scheme pw_schemes_default[] = +{ +#ifdef LUTIL_SHA1_BYTES + { BER_BVC("{SSHA}"), chk_ssha1, hash_ssha1 }, + { BER_BVC("{SHA}"), chk_sha1, hash_sha1 }, +#endif + + { BER_BVC("{SMD5}"), chk_smd5, hash_smd5 }, + { BER_BVC("{MD5}"), chk_md5, hash_md5 }, + +#ifdef SLAPD_CRYPT + { BER_BVC("{CRYPT}"), chk_crypt, hash_crypt }, +# if defined( HAVE_GETPWNAM ) && defined( HAVE_STRUCT_PASSWD_PW_PASSWD ) + { BER_BVC("{UNIX}"), chk_unix, NULL }, +# endif +#endif + +#ifdef SLAPD_CLEARTEXT + /* pseudo scheme */ + { BER_BVC("{CLEARTEXT}"), NULL, hash_clear }, +#endif + + { BER_BVNULL, NULL, NULL } +}; + +int lutil_passwd_add( + struct berval *scheme, + LUTIL_PASSWD_CHK_FUNC *chk, + LUTIL_PASSWD_HASH_FUNC *hash ) +{ + struct pw_slist *ptr; + + if (!pw_inited) lutil_passwd_init(); + + ptr = ber_memalloc( sizeof( struct pw_slist )); + if (!ptr) return -1; + ptr->next = pw_schemes; + ptr->s.name = *scheme; + ptr->s.chk_fn = chk; + ptr->s.hash_fn = hash; + pw_schemes = ptr; + return 0; +} + +void lutil_passwd_init() +{ + struct pw_scheme *s; + + pw_inited = 1; + + for( s=(struct pw_scheme *)pw_schemes_default; s->name.bv_val; s++) { + if ( lutil_passwd_add( &s->name, s->chk_fn, s->hash_fn ) ) break; + } +} + +void lutil_passwd_destroy() +{ + struct pw_slist *ptr, *next; + + for( ptr=pw_schemes; ptr; ptr=next ) { + next = ptr->next; + ber_memfree( ptr ); + } +} + +static const struct pw_scheme *get_scheme( + const char* scheme ) +{ + struct pw_slist *pws; + struct berval bv; + + if (!pw_inited) lutil_passwd_init(); + + bv.bv_val = strchr( scheme, '}' ); + if ( !bv.bv_val ) + return NULL; + + bv.bv_len = bv.bv_val - scheme + 1; + bv.bv_val = (char *) scheme; + + for( pws=pw_schemes; pws; pws=pws->next ) { + if ( ber_bvstrcasecmp(&bv, &pws->s.name ) == 0 ) { + return &(pws->s); + } + } + + return NULL; +} + +int lutil_passwd_scheme( + const char* scheme ) +{ + if( scheme == NULL ) { + return 0; + } + + return get_scheme(scheme) != NULL; +} + + +static int is_allowed_scheme( + const char* scheme, + const char** schemes ) +{ + int i; + + if( schemes == NULL ) return 1; + + for( i=0; schemes[i] != NULL; i++ ) { + if( strcasecmp( scheme, schemes[i] ) == 0 ) { + return 1; + } + } + return 0; +} + +static struct berval *passwd_scheme( + const struct pw_scheme *scheme, + const struct berval * passwd, + struct berval *bv, + const char** allowed ) +{ + if( !is_allowed_scheme( scheme->name.bv_val, allowed ) ) { + return NULL; + } + + if( passwd->bv_len >= scheme->name.bv_len ) { + if( strncasecmp( passwd->bv_val, scheme->name.bv_val, scheme->name.bv_len ) == 0 ) { + bv->bv_val = &passwd->bv_val[scheme->name.bv_len]; + bv->bv_len = passwd->bv_len - scheme->name.bv_len; + + return bv; + } + } + + return NULL; +} + +/* + * Return 0 if creds are good. + */ +int +lutil_passwd( + const struct berval *passwd, /* stored passwd */ + const struct berval *cred, /* user cred */ + const char **schemes, + const char **text ) +{ + struct pw_slist *pws; + + if ( text ) *text = NULL; + + if (cred == NULL || cred->bv_len == 0 || + passwd == NULL || passwd->bv_len == 0 ) + { + return -1; + } + + if (!pw_inited) lutil_passwd_init(); + + for( pws=pw_schemes; pws; pws=pws->next ) { + if( pws->s.chk_fn ) { + struct berval x; + struct berval *p = passwd_scheme( &(pws->s), + passwd, &x, schemes ); + + if( p != NULL ) { + return (pws->s.chk_fn)( &(pws->s.name), p, cred, text ); + } + } + } + +#ifdef SLAPD_CLEARTEXT + /* Do we think there is a scheme specifier here that we + * didn't recognize? Assume a scheme name is at least 1 character. + */ + if (( passwd->bv_val[0] == '{' ) && + ( ber_bvchr( passwd, '}' ) > passwd->bv_val+1 )) + { + return 1; + } + if( is_allowed_scheme("{CLEARTEXT}", schemes ) ) { + return ( passwd->bv_len == cred->bv_len ) ? + memcmp( passwd->bv_val, cred->bv_val, passwd->bv_len ) + : 1; + } +#endif + return 1; +} + +int lutil_passwd_generate( struct berval *pw, ber_len_t len ) +{ + + if( len < 1 ) return -1; + + pw->bv_len = len; + pw->bv_val = ber_memalloc( len + 1 ); + + if( pw->bv_val == NULL ) { + return -1; + } + + if( lutil_entropy( (unsigned char *) pw->bv_val, pw->bv_len) < 0 ) { + return -1; + } + + for( len = 0; len < pw->bv_len; len++ ) { + pw->bv_val[len] = crypt64[ + pw->bv_val[len] % (sizeof(crypt64)-1) ]; + } + + pw->bv_val[len] = '\0'; + + return 0; +} + +int lutil_passwd_hash( + const struct berval * passwd, + const char * method, + struct berval *hash, + const char **text ) +{ + const struct pw_scheme *sc = get_scheme( method ); + + hash->bv_val = NULL; + hash->bv_len = 0; + + if( sc == NULL ) { + if( text ) *text = "scheme not recognized"; + return -1; + } + + if( ! sc->hash_fn ) { + if( text ) *text = "scheme provided no hash function"; + return -1; + } + + if( text ) *text = NULL; + + return (sc->hash_fn)( &sc->name, passwd, hash, text ); +} + +/* pw_string is only called when SLAPD_CRYPT is defined */ +#if defined(SLAPD_CRYPT) +static int pw_string( + const struct berval *sc, + struct berval *passwd ) +{ + struct berval pw; + + pw.bv_len = sc->bv_len + passwd->bv_len; + pw.bv_val = ber_memalloc( pw.bv_len + 1 ); + + if( pw.bv_val == NULL ) { + return LUTIL_PASSWD_ERR; + } + + AC_MEMCPY( pw.bv_val, sc->bv_val, sc->bv_len ); + AC_MEMCPY( &pw.bv_val[sc->bv_len], passwd->bv_val, passwd->bv_len ); + + pw.bv_val[pw.bv_len] = '\0'; + *passwd = pw; + + return LUTIL_PASSWD_OK; +} +#endif /* SLAPD_CRYPT */ + +int lutil_passwd_string64( + const struct berval *sc, + const struct berval *hash, + struct berval *b64, + const struct berval *salt ) +{ + int rc; + struct berval string; + size_t b64len; + + if( salt ) { + /* need to base64 combined string */ + string.bv_len = hash->bv_len + salt->bv_len; + string.bv_val = ber_memalloc( string.bv_len + 1 ); + + if( string.bv_val == NULL ) { + return LUTIL_PASSWD_ERR; + } + + AC_MEMCPY( string.bv_val, hash->bv_val, + hash->bv_len ); + AC_MEMCPY( &string.bv_val[hash->bv_len], salt->bv_val, + salt->bv_len ); + string.bv_val[string.bv_len] = '\0'; + + } else { + string = *hash; + } + + b64len = LUTIL_BASE64_ENCODE_LEN( string.bv_len ) + 1; + b64->bv_len = b64len + sc->bv_len; + b64->bv_val = ber_memalloc( b64->bv_len + 1 ); + + if( b64->bv_val == NULL ) { + if( salt ) ber_memfree( string.bv_val ); + return LUTIL_PASSWD_ERR; + } + + AC_MEMCPY(b64->bv_val, sc->bv_val, sc->bv_len); + + rc = lutil_b64_ntop( + (unsigned char *) string.bv_val, string.bv_len, + &b64->bv_val[sc->bv_len], b64len ); + + if( salt ) ber_memfree( string.bv_val ); + + if( rc < 0 ) { + return LUTIL_PASSWD_ERR; + } + + /* recompute length */ + b64->bv_len = sc->bv_len + rc; + assert( strlen(b64->bv_val) == b64->bv_len ); + return LUTIL_PASSWD_OK; +} + +/* PASSWORD CHECK ROUTINES */ + +#ifdef LUTIL_SHA1_BYTES +static int chk_ssha1( + const struct berval *sc, + const struct berval * passwd, + const struct berval * cred, + const char **text ) +{ + lutil_SHA1_CTX SHA1context; + unsigned char SHA1digest[LUTIL_SHA1_BYTES]; + int rc; + unsigned char *orig_pass = NULL; + size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len); + + /* safety check -- must have some salt */ + if (decode_len <= sizeof(SHA1digest)) { + return LUTIL_PASSWD_ERR; + } + + /* decode base64 password */ + orig_pass = (unsigned char *) ber_memalloc(decode_len + 1); + + if( orig_pass == NULL ) return LUTIL_PASSWD_ERR; + + rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len); + + /* safety check -- must have some salt */ + if (rc <= (int)(sizeof(SHA1digest))) { + ber_memfree(orig_pass); + return LUTIL_PASSWD_ERR; + } + + /* hash credentials with salt */ + lutil_SHA1Init(&SHA1context); + lutil_SHA1Update(&SHA1context, + (const unsigned char *) cred->bv_val, cred->bv_len); + lutil_SHA1Update(&SHA1context, + (const unsigned char *) &orig_pass[sizeof(SHA1digest)], + rc - sizeof(SHA1digest)); + lutil_SHA1Final(SHA1digest, &SHA1context); + + /* compare */ + rc = memcmp((char *)orig_pass, (char *)SHA1digest, sizeof(SHA1digest)); + ber_memfree(orig_pass); + return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK; +} + +static int chk_sha1( + const struct berval *sc, + const struct berval * passwd, + const struct berval * cred, + const char **text ) +{ + lutil_SHA1_CTX SHA1context; + unsigned char SHA1digest[LUTIL_SHA1_BYTES]; + int rc; + unsigned char *orig_pass = NULL; + size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len); + + /* safety check */ + if (decode_len < sizeof(SHA1digest)) { + return LUTIL_PASSWD_ERR; + } + + /* base64 un-encode password */ + orig_pass = (unsigned char *) ber_memalloc(decode_len + 1); + + if( orig_pass == NULL ) return LUTIL_PASSWD_ERR; + + rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len); + + if( rc != sizeof(SHA1digest) ) { + ber_memfree(orig_pass); + return LUTIL_PASSWD_ERR; + } + + /* hash credentials with salt */ + lutil_SHA1Init(&SHA1context); + lutil_SHA1Update(&SHA1context, + (const unsigned char *) cred->bv_val, cred->bv_len); + lutil_SHA1Final(SHA1digest, &SHA1context); + + /* compare */ + rc = memcmp((char *)orig_pass, (char *)SHA1digest, sizeof(SHA1digest)); + ber_memfree(orig_pass); + return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK; +} +#endif + +static int chk_smd5( + const struct berval *sc, + const struct berval * passwd, + const struct berval * cred, + const char **text ) +{ + lutil_MD5_CTX MD5context; + unsigned char MD5digest[LUTIL_MD5_BYTES]; + int rc; + unsigned char *orig_pass = NULL; + size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len); + + /* safety check */ + if (decode_len <= sizeof(MD5digest)) { + return LUTIL_PASSWD_ERR; + } + + /* base64 un-encode password */ + orig_pass = (unsigned char *) ber_memalloc(decode_len + 1); + + if( orig_pass == NULL ) return LUTIL_PASSWD_ERR; + + rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len); + + if (rc <= (int)(sizeof(MD5digest))) { + ber_memfree(orig_pass); + return LUTIL_PASSWD_ERR; + } + + /* hash credentials with salt */ + lutil_MD5Init(&MD5context); + lutil_MD5Update(&MD5context, + (const unsigned char *) cred->bv_val, + cred->bv_len ); + lutil_MD5Update(&MD5context, + &orig_pass[sizeof(MD5digest)], + rc - sizeof(MD5digest)); + lutil_MD5Final(MD5digest, &MD5context); + + /* compare */ + rc = memcmp((char *)orig_pass, (char *)MD5digest, sizeof(MD5digest)); + ber_memfree(orig_pass); + return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK; +} + +static int chk_md5( + const struct berval *sc, + const struct berval * passwd, + const struct berval * cred, + const char **text ) +{ + lutil_MD5_CTX MD5context; + unsigned char MD5digest[LUTIL_MD5_BYTES]; + int rc; + unsigned char *orig_pass = NULL; + size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len); + + /* safety check */ + if (decode_len < sizeof(MD5digest)) { + return LUTIL_PASSWD_ERR; + } + + /* base64 un-encode password */ + orig_pass = (unsigned char *) ber_memalloc(decode_len + 1); + + if( orig_pass == NULL ) return LUTIL_PASSWD_ERR; + + rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len); + if ( rc != sizeof(MD5digest) ) { + ber_memfree(orig_pass); + return LUTIL_PASSWD_ERR; + } + + /* hash credentials with salt */ + lutil_MD5Init(&MD5context); + lutil_MD5Update(&MD5context, + (const unsigned char *) cred->bv_val, + cred->bv_len ); + lutil_MD5Final(MD5digest, &MD5context); + + /* compare */ + rc = memcmp((char *)orig_pass, (char *)MD5digest, sizeof(MD5digest)); + ber_memfree(orig_pass); + return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK; +} + +#ifdef SLAPD_CRYPT +static int lutil_crypt( + const char *key, + const char *salt, + char **hash ) +{ + char *cr = crypt( key, salt ); + int rc; + + if( cr == NULL || cr[0] == '\0' ) { + /* salt must have been invalid */ + rc = LUTIL_PASSWD_ERR; + } else { + if ( hash ) { + *hash = ber_strdup( cr ); + rc = LUTIL_PASSWD_OK; + } else { + rc = strcmp( salt, cr ) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK; + } + } + return rc; +} + +static int chk_crypt( + const struct berval *sc, + const struct berval * passwd, + const struct berval * cred, + const char **text ) +{ + unsigned int i; + + for( i=0; i<cred->bv_len; i++) { + if(cred->bv_val[i] == '\0') { + return LUTIL_PASSWD_ERR; /* NUL character in password */ + } + } + + if( cred->bv_val[i] != '\0' ) { + return LUTIL_PASSWD_ERR; /* cred must behave like a string */ + } + + if( passwd->bv_len < 2 ) { + return LUTIL_PASSWD_ERR; /* passwd must be at least two characters long */ + } + + for( i=0; i<passwd->bv_len; i++) { + if(passwd->bv_val[i] == '\0') { + return LUTIL_PASSWD_ERR; /* NUL character in password */ + } + } + + if( passwd->bv_val[i] != '\0' ) { + return LUTIL_PASSWD_ERR; /* passwd must behave like a string */ + } + + return lutil_cryptptr( cred->bv_val, passwd->bv_val, NULL ); +} + +# if defined( HAVE_GETPWNAM ) && defined( HAVE_STRUCT_PASSWD_PW_PASSWD ) +static int chk_unix( + const struct berval *sc, + const struct berval * passwd, + const struct berval * cred, + const char **text ) +{ + unsigned int i; + char *pw; + + for( i=0; i<cred->bv_len; i++) { + if(cred->bv_val[i] == '\0') { + return LUTIL_PASSWD_ERR; /* NUL character in password */ + } + } + if( cred->bv_val[i] != '\0' ) { + return LUTIL_PASSWD_ERR; /* cred must behave like a string */ + } + + for( i=0; i<passwd->bv_len; i++) { + if(passwd->bv_val[i] == '\0') { + return LUTIL_PASSWD_ERR; /* NUL character in password */ + } + } + + if( passwd->bv_val[i] != '\0' ) { + return LUTIL_PASSWD_ERR; /* passwd must behave like a string */ + } + + { + struct passwd *pwd = getpwnam(passwd->bv_val); + + if(pwd == NULL) { + return LUTIL_PASSWD_ERR; /* not found */ + } + + pw = pwd->pw_passwd; + } +# ifdef HAVE_GETSPNAM + { + struct spwd *spwd = getspnam(passwd->bv_val); + + if(spwd != NULL) { + pw = spwd->sp_pwdp; + } + } +# endif +# ifdef HAVE_AIX_SECURITY + { + struct userpw *upw = getuserpw(passwd->bv_val); + + if (upw != NULL) { + pw = upw->upw_passwd; + } + } +# endif + + if( pw == NULL || pw[0] == '\0' || pw[1] == '\0' ) { + /* password must must be at least two characters long */ + return LUTIL_PASSWD_ERR; + } + + return lutil_cryptptr( cred->bv_val, pw, NULL ); +} +# endif +#endif + +/* PASSWORD GENERATION ROUTINES */ + +#ifdef LUTIL_SHA1_BYTES +static int hash_ssha1( + const struct berval *scheme, + const struct berval *passwd, + struct berval *hash, + const char **text ) +{ + lutil_SHA1_CTX SHA1context; + unsigned char SHA1digest[LUTIL_SHA1_BYTES]; + char saltdata[SALT_SIZE]; + struct berval digest; + struct berval salt; + + digest.bv_val = (char *) SHA1digest; + digest.bv_len = sizeof(SHA1digest); + salt.bv_val = saltdata; + salt.bv_len = sizeof(saltdata); + + if( lutil_entropy( (unsigned char *) salt.bv_val, salt.bv_len) < 0 ) { + return LUTIL_PASSWD_ERR; + } + + lutil_SHA1Init( &SHA1context ); + lutil_SHA1Update( &SHA1context, + (const unsigned char *)passwd->bv_val, passwd->bv_len ); + lutil_SHA1Update( &SHA1context, + (const unsigned char *)salt.bv_val, salt.bv_len ); + lutil_SHA1Final( SHA1digest, &SHA1context ); + + return lutil_passwd_string64( scheme, &digest, hash, &salt); +} + +static int hash_sha1( + const struct berval *scheme, + const struct berval *passwd, + struct berval *hash, + const char **text ) +{ + lutil_SHA1_CTX SHA1context; + unsigned char SHA1digest[LUTIL_SHA1_BYTES]; + struct berval digest; + digest.bv_val = (char *) SHA1digest; + digest.bv_len = sizeof(SHA1digest); + + lutil_SHA1Init( &SHA1context ); + lutil_SHA1Update( &SHA1context, + (const unsigned char *)passwd->bv_val, passwd->bv_len ); + lutil_SHA1Final( SHA1digest, &SHA1context ); + + return lutil_passwd_string64( scheme, &digest, hash, NULL); +} +#endif + +static int hash_smd5( + const struct berval *scheme, + const struct berval *passwd, + struct berval *hash, + const char **text ) +{ + lutil_MD5_CTX MD5context; + unsigned char MD5digest[LUTIL_MD5_BYTES]; + char saltdata[SALT_SIZE]; + struct berval digest; + struct berval salt; + + digest.bv_val = (char *) MD5digest; + digest.bv_len = sizeof(MD5digest); + salt.bv_val = saltdata; + salt.bv_len = sizeof(saltdata); + + if( lutil_entropy( (unsigned char *) salt.bv_val, salt.bv_len) < 0 ) { + return LUTIL_PASSWD_ERR; + } + + lutil_MD5Init( &MD5context ); + lutil_MD5Update( &MD5context, + (const unsigned char *) passwd->bv_val, passwd->bv_len ); + lutil_MD5Update( &MD5context, + (const unsigned char *) salt.bv_val, salt.bv_len ); + lutil_MD5Final( MD5digest, &MD5context ); + + return lutil_passwd_string64( scheme, &digest, hash, &salt ); +} + +static int hash_md5( + const struct berval *scheme, + const struct berval *passwd, + struct berval *hash, + const char **text ) +{ + lutil_MD5_CTX MD5context; + unsigned char MD5digest[LUTIL_MD5_BYTES]; + + struct berval digest; + + digest.bv_val = (char *) MD5digest; + digest.bv_len = sizeof(MD5digest); + + lutil_MD5Init( &MD5context ); + lutil_MD5Update( &MD5context, + (const unsigned char *) passwd->bv_val, passwd->bv_len ); + lutil_MD5Final( MD5digest, &MD5context ); + + return lutil_passwd_string64( scheme, &digest, hash, NULL ); +; +} + +#ifdef SLAPD_CRYPT +static int hash_crypt( + const struct berval *scheme, + const struct berval *passwd, + struct berval *hash, + const char **text ) +{ + unsigned char salt[32]; /* salt suitable for most anything */ + unsigned int i; + char *save; + int rc; + + for( i=0; i<passwd->bv_len; i++) { + if(passwd->bv_val[i] == '\0') { + return LUTIL_PASSWD_ERR; /* NUL character in password */ + } + } + + if( passwd->bv_val[i] != '\0' ) { + return LUTIL_PASSWD_ERR; /* passwd must behave like a string */ + } + + if( lutil_entropy( salt, sizeof( salt ) ) < 0 ) { + return LUTIL_PASSWD_ERR; + } + + for( i=0; i< ( sizeof(salt) - 1 ); i++ ) { + salt[i] = crypt64[ salt[i] % (sizeof(crypt64)-1) ]; + } + salt[sizeof( salt ) - 1 ] = '\0'; + + if( salt_format != NULL ) { + /* copy the salt we made into entropy before snprintfing + it back into the salt */ + char entropy[sizeof(salt)]; + strcpy( entropy, (char *) salt ); + snprintf( (char *) salt, sizeof(entropy), salt_format, entropy ); + } + + rc = lutil_cryptptr( passwd->bv_val, (char *) salt, &hash->bv_val ); + if ( rc != LUTIL_PASSWD_OK ) return rc; + + if( hash->bv_val == NULL ) return -1; + + hash->bv_len = strlen( hash->bv_val ); + + save = hash->bv_val; + + if( hash->bv_len == 0 ) { + rc = LUTIL_PASSWD_ERR; + } else { + rc = pw_string( scheme, hash ); + } + ber_memfree( save ); + return rc; +} +#endif + +int lutil_salt_format(const char *format) +{ +#ifdef SLAPD_CRYPT + ber_memfree( salt_format ); + + salt_format = format != NULL ? ber_strdup( format ) : NULL; +#endif + + return 0; +} + +#ifdef SLAPD_CLEARTEXT +static int hash_clear( + const struct berval *scheme, + const struct berval *passwd, + struct berval *hash, + const char **text ) +{ + ber_dupbv( hash, (struct berval *)passwd ); + return LUTIL_PASSWD_OK; +} +#endif + diff --git a/libraries/liblutil/ptest.c b/libraries/liblutil/ptest.c new file mode 100644 index 0000000..5477007 --- /dev/null +++ b/libraries/liblutil/ptest.c @@ -0,0 +1,112 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * 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 + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/ctype.h> +#include <ac/signal.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include <lber.h> + +#include "lutil.h" + +/* + * Password Test Program + */ + +static char *hash[] = { +#ifdef SLAP_AUTHPASSWD + "SHA1", "MD5", +#else +#ifdef SLAPD_CRYPT + "{CRYPT}", +#endif + "{SSHA}", "{SMD5}", + "{SHA}", "{MD5}", + "{BOGUS}", +#endif + NULL +}; + +static struct berval pw[] = { + { sizeof("secret")-1, "secret" }, + { sizeof("binary\0secret")-1, "binary\0secret" }, + { 0, NULL } +}; + +int +main( int argc, char *argv[] ) +{ + int i, j, rc; + struct berval *passwd; +#ifdef SLAP_AUTHPASSWD + struct berval *salt; +#endif + struct berval bad; + bad.bv_val = "bad password"; + bad.bv_len = sizeof("bad password")-1; + + for( i= 0; hash[i]; i++ ) { + for( j = 0; pw[j].bv_len; j++ ) { +#ifdef SLAP_AUTHPASSWD + rc = lutil_authpasswd_hash( &pw[j], + &passwd, &salt, hash[i] ); + + if( rc ) +#else + passwd = lutil_passwd_hash( &pw[j], hash[i] ); + + if( passwd == NULL ) +#endif + { + printf("%s generate fail: %s (%d)\n", + hash[i], pw[j].bv_val, pw[j].bv_len ); + continue; + } + + +#ifdef SLAP_AUTHPASSWD + rc = lutil_authpasswd( &pw[j], passwd, salt, NULL ); +#else + rc = lutil_passwd( passwd, &pw[j], NULL ); +#endif + + printf("%s (%d): %s (%d)\t(%d) %s\n", + pw[j].bv_val, pw[j].bv_len, passwd->bv_val, passwd->bv_len, + rc, rc == 0 ? "OKAY" : "BAD" ); + +#ifdef SLAP_AUTHPASSWD + rc = lutil_authpasswd( passwd, salt, &bad, NULL ); +#else + rc = lutil_passwd( passwd, &bad, NULL ); +#endif + + printf("%s (%d): %s (%d)\t(%d) %s\n", + bad.bv_val, bad.bv_len, passwd->bv_val, passwd->bv_len, + rc, rc != 0 ? "OKAY" : "BAD" ); + } + + printf("\n"); + } + + return EXIT_SUCCESS; +} diff --git a/libraries/liblutil/sasl.c b/libraries/liblutil/sasl.c new file mode 100644 index 0000000..b6a3f00 --- /dev/null +++ b/libraries/liblutil/sasl.c @@ -0,0 +1,232 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * 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 + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#ifdef HAVE_CYRUS_SASL + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/unistd.h> + +#ifdef HAVE_SASL_SASL_H +#include <sasl/sasl.h> +#else +#include <sasl.h> +#endif + +#include <ldap.h> +#include "ldap_pvt.h" +#include "lutil_ldap.h" + + +typedef struct lutil_sasl_defaults_s { + char *mech; + char *realm; + char *authcid; + char *passwd; + char *authzid; + char **resps; + int nresps; +} lutilSASLdefaults; + + +void +lutil_sasl_freedefs( + void *defaults ) +{ + lutilSASLdefaults *defs = defaults; + + assert( defs != NULL ); + + if (defs->mech) ber_memfree(defs->mech); + if (defs->realm) ber_memfree(defs->realm); + if (defs->authcid) ber_memfree(defs->authcid); + if (defs->passwd) ber_memfree(defs->passwd); + if (defs->authzid) ber_memfree(defs->authzid); + if (defs->resps) ldap_charray_free(defs->resps); + + ber_memfree(defs); +} + +void * +lutil_sasl_defaults( + LDAP *ld, + char *mech, + char *realm, + char *authcid, + char *passwd, + char *authzid ) +{ + lutilSASLdefaults *defaults; + + defaults = ber_memalloc( sizeof( lutilSASLdefaults ) ); + + if( defaults == NULL ) return NULL; + + defaults->mech = mech ? ber_strdup(mech) : NULL; + defaults->realm = realm ? ber_strdup(realm) : NULL; + defaults->authcid = authcid ? ber_strdup(authcid) : NULL; + defaults->passwd = passwd ? ber_strdup(passwd) : NULL; + defaults->authzid = authzid ? ber_strdup(authzid) : NULL; + + if( defaults->mech == NULL ) { + ldap_get_option( ld, LDAP_OPT_X_SASL_MECH, &defaults->mech ); + } + if( defaults->realm == NULL ) { + ldap_get_option( ld, LDAP_OPT_X_SASL_REALM, &defaults->realm ); + } + if( defaults->authcid == NULL ) { + ldap_get_option( ld, LDAP_OPT_X_SASL_AUTHCID, &defaults->authcid ); + } + if( defaults->authzid == NULL ) { + ldap_get_option( ld, LDAP_OPT_X_SASL_AUTHZID, &defaults->authzid ); + } + defaults->resps = NULL; + defaults->nresps = 0; + + return defaults; +} + +static int interaction( + unsigned flags, + sasl_interact_t *interact, + lutilSASLdefaults *defaults ) +{ + const char *dflt = interact->defresult; + char input[1024]; + + int noecho=0; + int challenge=0; + + switch( interact->id ) { + case SASL_CB_GETREALM: + if( defaults ) dflt = defaults->realm; + break; + case SASL_CB_AUTHNAME: + if( defaults ) dflt = defaults->authcid; + break; + case SASL_CB_PASS: + if( defaults ) dflt = defaults->passwd; + noecho = 1; + break; + case SASL_CB_USER: + if( defaults ) dflt = defaults->authzid; + break; + case SASL_CB_NOECHOPROMPT: + noecho = 1; + challenge = 1; + break; + case SASL_CB_ECHOPROMPT: + challenge = 1; + break; + } + + if( dflt && !*dflt ) dflt = NULL; + + if( flags != LDAP_SASL_INTERACTIVE && + ( dflt || interact->id == SASL_CB_USER ) ) + { + goto use_default; + } + + if( flags == LDAP_SASL_QUIET ) { + /* don't prompt */ + return LDAP_OTHER; + } + + if( challenge ) { + if( interact->challenge ) { + fprintf( stderr, _("Challenge: %s\n"), interact->challenge ); + } + } + + if( dflt ) { + fprintf( stderr, _("Default: %s\n"), dflt ); + } + + snprintf( input, sizeof input, "%s: ", + interact->prompt ? interact->prompt : _("Interact") ); + + if( noecho ) { + interact->result = (char *) getpassphrase( input ); + interact->len = interact->result + ? strlen( interact->result ) : 0; + + } else { + /* prompt user */ + fputs( input, stderr ); + + /* get input */ + interact->result = fgets( input, sizeof(input), stdin ); + + if( interact->result == NULL ) { + interact->len = 0; + return LDAP_UNAVAILABLE; + } + + /* len of input */ + interact->len = strlen(input); + + if( interact->len > 0 && input[interact->len - 1] == '\n' ) { + /* input includes '\n', trim it */ + interact->len--; + input[interact->len] = '\0'; + } + } + + + if( interact->len > 0 ) { + /* duplicate */ + char *p = (char *)interact->result; + ldap_charray_add(&defaults->resps, interact->result); + interact->result = defaults->resps[defaults->nresps++]; + + /* zap */ + memset( p, '\0', interact->len ); + + } else { +use_default: + /* input must be empty */ + interact->result = (dflt && *dflt) ? dflt : ""; + interact->len = strlen( interact->result ); + } + + return LDAP_SUCCESS; +} + +int lutil_sasl_interact( + LDAP *ld, + unsigned flags, + void *defaults, + void *in ) +{ + sasl_interact_t *interact = in; + + if( flags == LDAP_SASL_INTERACTIVE ) { + fputs( _("SASL Interaction\n"), stderr ); + } + + while( interact->id != SASL_CB_LIST_END ) { + int rc = interaction( flags, interact, defaults ); + + if( rc ) return rc; + interact++; + } + + return LDAP_SUCCESS; +} +#endif diff --git a/libraries/liblutil/sha1.c b/libraries/liblutil/sha1.c new file mode 100644 index 0000000..de71244 --- /dev/null +++ b/libraries/liblutil/sha1.c @@ -0,0 +1,288 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * 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 + * <http://www.OpenLDAP.org/license.html>. + */ +/* This work was derived from code developed by Steve Reid and + * adapted for use in OpenLDAP by Kurt D. Zeilenga. + */ + + +/* Acquired from: + * $OpenBSD: sha1.c,v 1.9 1997/07/23 21:12:32 kstailey Exp $ */ +/* + * SHA-1 in C + * By Steve Reid <steve@edmweb.com> + * 100% Public Domain + * + * Test Vectors (from FIPS PUB 180-1) + * "abc" + * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D + * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 + * A million repetitions of "a" + * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F + */ +/* + * This code assumes uint32 is 32 bits and char is 8 bits + */ + +#include "portable.h" +#include <ac/param.h> +#include <ac/string.h> +#include <ac/socket.h> +#include <ac/bytes.h> + +#include "lutil_sha1.h" + +#ifdef LUTIL_SHA1_BYTES + +/* undefining this will cause pointer alignment errors */ +#define SHA1HANDSOFF /* Copies data before messing with it. */ +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* + * blk0() and blk() perform the initial expand. + * I got the idea of expanding during the round function from SSLeay + */ +#if BYTE_ORDER == LITTLE_ENDIAN +# define blk0(i) (block[i] = (rol(block[i],24)&0xFF00FF00) \ + |(rol(block[i],8)&0x00FF00FF)) +#else +# define blk0(i) block[i] +#endif +#define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \ + ^block[(i+2)&15]^block[i&15],1)) + +/* + * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 + */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +/* + * Hash a single 512-bit block. This is the core of the algorithm. + */ +void +lutil_SHA1Transform( uint32 *state, const unsigned char *buffer ) +{ + uint32 a, b, c, d, e; + +#ifdef SHA1HANDSOFF + uint32 block[16]; + (void)AC_MEMCPY(block, buffer, 64); +#else + uint32 *block = (u_int32 *) buffer; +#endif + + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + + /* Wipe variables */ + a = b = c = d = e = 0; +} + + +/* + * lutil_SHA1Init - Initialize new context + */ +void +lutil_SHA1Init( lutil_SHA1_CTX *context ) +{ + + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* + * Run your data through this. + */ +void +lutil_SHA1Update( + lutil_SHA1_CTX *context, + const unsigned char *data, + uint32 len +) +{ + unsigned int i, j; + + j = context->count[0]; + if ((context->count[0] += len << 3) < j) + context->count[1] += (len>>29)+1; + j = (j >> 3) & 63; + if ((j + len) > 63) { + (void)AC_MEMCPY(&context->buffer[j], data, (i = 64-j)); + lutil_SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) + lutil_SHA1Transform(context->state, &data[i]); + j = 0; + } else { + i = 0; + } + (void)AC_MEMCPY(&context->buffer[j], &data[i], len - i); +} + + +/* + * Add padding and return the message digest. + */ +void +lutil_SHA1Final( unsigned char *digest, lutil_SHA1_CTX *context ) +{ + unsigned int i; + unsigned char finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + lutil_SHA1Update(context, (unsigned char *)"\200", 1); + while ((context->count[0] & 504) != 448) + lutil_SHA1Update(context, (unsigned char *)"\0", 1); + lutil_SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ + + if (digest) { + for (i = 0; i < 20; i++) + digest[i] = (unsigned char) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } +} + + +/* sha1hl.c + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * <phk@login.dkuug.dk> wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char rcsid[] = "$OpenBSD: sha1hl.c,v 1.1 1997/07/12 20:06:03 millert Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include <stdio.h> +#include <ac/stdlib.h> + +#include <ac/errno.h> +#include <ac/unistd.h> + +#ifdef HAVE_SYS_FILE_H +#include <sys/file.h> +#endif + +#ifdef HAVE_IO_H +#include <io.h> +#endif + +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + + +/* ARGSUSED */ +char * +lutil_SHA1End( lutil_SHA1_CTX *ctx, char *buf ) +{ + int i; + char *p = buf; + unsigned char digest[20]; + static const char hex[]="0123456789abcdef"; + + if (p == NULL && (p = malloc(41)) == NULL) + return 0; + + lutil_SHA1Final(digest,ctx); + for (i = 0; i < 20; i++) { + p[i + i] = hex[digest[i] >> 4]; + p[i + i + 1] = hex[digest[i] & 0x0f]; + } + p[i + i] = '\0'; + return(p); +} + +char * +lutil_SHA1File( char *filename, char *buf ) +{ + unsigned char buffer[BUFSIZ]; + lutil_SHA1_CTX ctx; + int fd, num, oerrno; + + lutil_SHA1Init(&ctx); + + if ((fd = open(filename,O_RDONLY)) < 0) + return(0); + + while ((num = read(fd, buffer, sizeof(buffer))) > 0) + lutil_SHA1Update(&ctx, buffer, num); + + oerrno = errno; + close(fd); + errno = oerrno; + return(num < 0 ? 0 : lutil_SHA1End(&ctx, buf)); +} + +char * +lutil_SHA1Data( const unsigned char *data, size_t len, char *buf ) +{ + lutil_SHA1_CTX ctx; + + lutil_SHA1Init(&ctx); + lutil_SHA1Update(&ctx, data, len); + return(lutil_SHA1End(&ctx, buf)); +} + +#endif diff --git a/libraries/liblutil/signal.c b/libraries/liblutil/signal.c new file mode 100644 index 0000000..9d9da7a --- /dev/null +++ b/libraries/liblutil/signal.c @@ -0,0 +1,41 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * 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 + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#ifdef HAVE_SIGACTION +#include <ac/string.h> +#include <ac/signal.h> + +lutil_sig_t +lutil_sigaction(int sig, lutil_sig_t func) +{ + struct sigaction action, oaction; + + memset( &action, '\0', sizeof(action) ); + + action.sa_handler = func; + sigemptyset( &action.sa_mask ); +#ifdef SA_RESTART + action.sa_flags |= SA_RESTART; +#endif + + if( sigaction( sig, &action, &oaction ) != 0 ) { + return NULL; + } + + return oaction.sa_handler; +} +#endif diff --git a/libraries/liblutil/slapdmsg.bin b/libraries/liblutil/slapdmsg.bin Binary files differnew file mode 100644 index 0000000..d8ca806 --- /dev/null +++ b/libraries/liblutil/slapdmsg.bin diff --git a/libraries/liblutil/slapdmsg.h b/libraries/liblutil/slapdmsg.h new file mode 100644 index 0000000..a307603 --- /dev/null +++ b/libraries/liblutil/slapdmsg.h @@ -0,0 +1,65 @@ +// +// This file contains message strings for the OpenLDAP slapd service. +// +// This file should be compiled as follows +// mc -v slapdmsg.mc -r $(IntDir) +// rc /v /r $(IntDir)\slapdmsg.rc +// The mc (message compiler) command generates the .rc and .h files from this file. The +// rc (resource compiler) takes the .rc file and produces a .res file that can be linked +// with the final executable application. The application is then registered as a message +// source with by creating the appropriate entries in the system registry. +// +// +// Values are 32 bit values laid out as follows: +// +// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +---+-+-+-----------------------+-------------------------------+ +// |Sev|C|R| Facility | Code | +// +---+-+-+-----------------------+-------------------------------+ +// +// where +// +// Sev - is the severity code +// +// 00 - Success +// 01 - Informational +// 10 - Warning +// 11 - Error +// +// C - is the Customer code flag +// +// R - is a reserved bit +// +// Facility - is the facility code +// +// Code - is the facility's status code +// +// +// Define the facility codes +// + + +// +// Define the severity codes +// + + +// +// MessageId: MSG_SVC_STARTED +// +// MessageText: +// +// OpenLDAP service started. debuglevel=%1, conffile=%2, urls=%3 +// +#define MSG_SVC_STARTED 0x40000500L + +// +// MessageId: MSG_SVC_STOPPED +// +// MessageText: +// +// OpenLDAP service stopped. +// +#define MSG_SVC_STOPPED 0x40000501L + diff --git a/libraries/liblutil/slapdmsg.mc b/libraries/liblutil/slapdmsg.mc new file mode 100644 index 0000000..53401f0 --- /dev/null +++ b/libraries/liblutil/slapdmsg.mc @@ -0,0 +1,28 @@ +;// +;// This file contains message strings for the OpenLDAP slapd service. +;// +;// This file should be compiled as follows +;// mc -v slapdmsg.mc -r $(IntDir) +;// rc /v /r $(IntDir)\slapdmsg.rc +;// The mc (message compiler) command generates the .rc and .h files from this file. The +;// rc (resource compiler) takes the .rc file and produces a .res file that can be linked +;// with the final executable application. The application is then registered as a message +;// source with by creating the appropriate entries in the system registry. +;// + +MessageID=0x500 +Severity=Informational +SymbolicName=MSG_SVC_STARTED +Facility=Application +Language=English +OpenLDAP service started. debuglevel=%1, conffile=%2, urls=%3 +. + + +MessageID=0x501 +Severity=Informational +SymbolicName=MSG_SVC_STOPPED +Facility=Application +Language=English +OpenLDAP service stopped. +. diff --git a/libraries/liblutil/slapdmsg.rc b/libraries/liblutil/slapdmsg.rc new file mode 100644 index 0000000..f967de2 --- /dev/null +++ b/libraries/liblutil/slapdmsg.rc @@ -0,0 +1,2 @@ +LANGUAGE 0x9,0x1 +1 11 slapdmsg.bin diff --git a/libraries/liblutil/sockpair.c b/libraries/liblutil/sockpair.c new file mode 100644 index 0000000..7be096d --- /dev/null +++ b/libraries/liblutil/sockpair.c @@ -0,0 +1,78 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * 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 + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" +#include <ac/socket.h> +#include <ac/unistd.h> + +#include <lutil.h> + +/* Return a pair of socket descriptors that are connected to each other. + * The returned descriptors are suitable for use with select(). The two + * descriptors may or may not be identical; the function may return + * the same descriptor number in both slots. It is guaranteed that + * data written on sds[1] will be readable on sds[0]. The returned + * descriptors may be datagram oriented, so data should be written + * in reasonably small pieces and read all at once. On Unix systems + * this function is best implemented using a single pipe() call. + */ + +int lutil_pair( ber_socket_t sds[2] ) +{ +#ifdef USE_PIPE + return pipe( sds ); +#else + struct sockaddr_in si; + int rc; + ber_socklen_t len = sizeof(si); + ber_socket_t sd; + + sd = socket( AF_INET, SOCK_DGRAM, 0 ); + if ( sd == AC_SOCKET_INVALID ) { + return sd; + } + + (void) memset( (void*) &si, '\0', len ); + si.sin_family = AF_INET; + si.sin_port = 0; + si.sin_addr.s_addr = htonl( INADDR_LOOPBACK ); + + rc = bind( sd, (struct sockaddr *)&si, len ); + if ( rc == AC_SOCKET_ERROR ) { + tcp_close(sd); + return rc; + } + + rc = getsockname( sd, (struct sockaddr *)&si, &len ); + if ( rc == AC_SOCKET_ERROR ) { + tcp_close(sd); + return rc; + } + + rc = connect( sd, (struct sockaddr *)&si, len ); + if ( rc == AC_SOCKET_ERROR ) { + tcp_close(sd); + return rc; + } + + sds[0] = sd; +#if !HAVE_WINSOCK + sds[1] = dup( sds[0] ); +#else + sds[1] = sds[0]; +#endif + return 0; +#endif +} diff --git a/libraries/liblutil/utils.c b/libraries/liblutil/utils.c new file mode 100644 index 0000000..ea80659 --- /dev/null +++ b/libraries/liblutil/utils.c @@ -0,0 +1,1071 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2022 The OpenLDAP Foundation. + * 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 + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <limits.h> +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/stdarg.h> +#include <ac/string.h> +#include <ac/ctype.h> +#include <ac/unistd.h> +#include <ac/time.h> +#include <ac/errno.h> +#ifdef HAVE_IO_H +#include <io.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef _WIN32 +#include <windows.h> +#endif + +#include "lutil.h" +#include "ldap_defaults.h" +#include "ldap_pvt.h" +#include "lber_pvt.h" + +#ifdef HAVE_EBCDIC +int _trans_argv = 1; +#endif + +#ifdef _WIN32 +/* Some Windows versions accept both forward and backslashes in + * directory paths, but we always use backslashes when generating + * and parsing... + */ +void lutil_slashpath( char *path ) +{ + char *c, *p; + + p = path; + while (( c=strchr( p, '/' ))) { + *c++ = '\\'; + p = c; + } +} +#endif + +char* lutil_progname( const char* name, int argc, char *argv[] ) +{ + char *progname; + + if(argc == 0) { + return (char *)name; + } + +#ifdef HAVE_EBCDIC + if (_trans_argv) { + int i; + for (i=0; i<argc; i++) __etoa(argv[i]); + _trans_argv = 0; + } +#endif + LUTIL_SLASHPATH( argv[0] ); + progname = strrchr ( argv[0], *LDAP_DIRSEP ); + progname = progname ? &progname[1] : argv[0]; +#ifdef _WIN32 + { + size_t len = strlen( progname ); + if ( len > 4 && strcasecmp( &progname[len - 4], ".exe" ) == 0 ) + progname[len - 4] = '\0'; + } +#endif + return progname; +} + +#if 0 +size_t lutil_gentime( char *s, size_t smax, const struct tm *tm ) +{ + size_t ret; +#ifdef HAVE_EBCDIC +/* We've been compiling in ASCII so far, but we want EBCDIC now since + * strftime only understands EBCDIC input. + */ +#pragma convlit(suspend) +#endif + ret = strftime( s, smax, "%Y%m%d%H%M%SZ", tm ); +#ifdef HAVE_EBCDIC +#pragma convlit(resume) + __etoa( s ); +#endif + return ret; +} +#endif + +size_t lutil_localtime( char *s, size_t smax, const struct tm *tm, long delta ) +{ + size_t ret; + char *p; + + if ( smax < 16 ) { /* YYYYmmddHHMMSSZ */ + return 0; + } + +#ifdef HAVE_EBCDIC +/* We've been compiling in ASCII so far, but we want EBCDIC now since + * strftime only understands EBCDIC input. + */ +#pragma convlit(suspend) +#endif + ret = strftime( s, smax, "%Y%m%d%H%M%SZ", tm ); +#ifdef HAVE_EBCDIC +#pragma convlit(resume) + __etoa( s ); +#endif + if ( delta == 0 || ret == 0 ) { + return ret; + } + + if ( smax < 20 ) { /* YYYYmmddHHMMSS+HHMM */ + return 0; + } + + p = s + 14; + + if ( delta < 0 ) { + p[ 0 ] = '-'; + delta = -delta; + } else { + p[ 0 ] = '+'; + } + p++; + + snprintf( p, smax - 15, "%02ld%02ld", delta / 3600, + ( delta % 3600 ) / 60 ); + + return ret + 4; +} + +int lutil_tm2time( struct lutil_tm *tm, struct lutil_timet *tt ) +{ + static int moffset[12] = { + 0, 31, 59, 90, 120, + 151, 181, 212, 243, + 273, 304, 334 }; + int sec; + + tt->tt_nsec = tm->tm_nsec; + + /* special case 0000/01/01+00:00:00 is returned as zero */ + if ( tm->tm_year == -1900 && tm->tm_mon == 0 && tm->tm_mday == 1 && + tm->tm_hour == 0 && tm->tm_min == 0 && tm->tm_sec == 0 ) { + tt->tt_sec = 0; + tt->tt_gsec = 0; + return 0; + } + + /* tm->tm_year is years since 1900 */ + /* calculate days from years since 1970 (epoch) */ + tt->tt_sec = tm->tm_year - 70; + tt->tt_sec *= 365L; + + /* count leap days in preceding years */ + tt->tt_sec += ((tm->tm_year -69) >> 2); + + /* calculate days from months */ + tt->tt_sec += moffset[tm->tm_mon]; + + /* add in this year's leap day, if any */ + if (((tm->tm_year & 3) == 0) && (tm->tm_mon > 1)) { + tt->tt_sec ++; + } + + /* add in days in this month */ + tt->tt_sec += (tm->tm_mday - 1); + + /* this function can handle a range of about 17408 years... */ + /* 86400 seconds in a day, divided by 128 = 675 */ + tt->tt_sec *= 675; + + /* move high 7 bits into tt_gsec */ + tt->tt_gsec = tt->tt_sec >> 25; + tt->tt_sec -= tt->tt_gsec << 25; + + /* get hours */ + sec = tm->tm_hour; + + /* convert to minutes */ + sec *= 60L; + sec += tm->tm_min; + + /* convert to seconds */ + sec *= 60L; + sec += tm->tm_sec; + + /* add remaining seconds */ + tt->tt_sec <<= 7; + tt->tt_sec += sec; + + /* return success */ + return 0; +} + +/* Proleptic Gregorian Calendar, 1BCE = year 0 */ + +int lutil_tm2gtime( struct lutil_tm *tm, struct lutil_timet *tt ) +{ + static int moffset[12] = { + 0, 31, 59, 90, 120, + 151, 181, 212, 243, + 273, 304, 334 }; + int sec, year; + long tmp; + + tt->tt_nsec = tm->tm_nsec; + + /* tm->tm_year is years since 1900 */ + /* calculate days from 0000 */ + year = tm->tm_year + 1900; + tmp = year * 365; + + /* add in leap days */ + sec = (year - 1) / 4; + tmp += sec; + sec /= 25; + tmp -= sec; + sec /= 4; + tmp += sec; + /* Year 0000 was a leap year */ + if (year > 0) + tmp++; + + /* calculate days from months */ + tmp += moffset[tm->tm_mon]; + + /* add in this year's leap day, if any */ + if (tm->tm_mon > 1) { + sec = (year % 4) ? 0 : (year % 100) ? 1 : (year % 400) ? 0 : 1; + tmp += sec; + } + + /* add in days in this month */ + tmp += (tm->tm_mday - 1); + + /* this function can handle a range of about 17408 years... */ + /* 86400 seconds in a day, divided by 128 = 675 */ + tmp *= 675; + + /* move high 7 bits into tt_gsec */ + tt->tt_gsec = tmp >> 25; + tmp -= tt->tt_gsec << 25; + + /* toggle sign bit, keep positive greater than negative */ + tt->tt_gsec &= 0x7f; + tt->tt_gsec ^= 0x40; + + /* get hours */ + sec = tm->tm_hour; + + /* convert to minutes */ + sec *= 60L; + sec += tm->tm_min; + + /* convert to seconds */ + sec *= 60L; + sec += tm->tm_sec; + + /* add remaining seconds */ + tmp <<= 7; + tmp += sec; + tt->tt_sec = tmp; + + /* return success */ + return 0; +} + +int lutil_parsetime( char *atm, struct lutil_tm *tm ) +{ + while (atm && tm) { + char *ptr; + unsigned i, fracs; + int neg = 0; + + if (*atm == '-') { + neg = 1; + atm++; + } + ptr = atm; + + /* Is the stamp reasonably long? */ + for (i=0; isdigit((unsigned char) atm[i]); i++); + if (i < sizeof("00000101000000")-1) + break; + + /* + * parse the time into a struct tm + */ + /* 4 digit year to year - 1900 */ + tm->tm_year = *ptr++ - '0'; + tm->tm_year *= 10; tm->tm_year += *ptr++ - '0'; + tm->tm_year *= 10; tm->tm_year += *ptr++ - '0'; + tm->tm_year *= 10; tm->tm_year += *ptr++ - '0'; + if (neg) + tm->tm_year = -tm->tm_year; + tm->tm_year -= 1900; + /* month 01-12 to 0-11 */ + tm->tm_mon = *ptr++ - '0'; + tm->tm_mon *=10; tm->tm_mon += *ptr++ - '0'; + if (tm->tm_mon < 1 || tm->tm_mon > 12) break; + tm->tm_mon--; + + /* day of month 01-31 */ + tm->tm_mday = *ptr++ - '0'; + tm->tm_mday *=10; tm->tm_mday += *ptr++ - '0'; + if (tm->tm_mday < 1 || tm->tm_mday > 31) break; + + /* Hour 00-23 */ + tm->tm_hour = *ptr++ - '0'; + tm->tm_hour *=10; tm->tm_hour += *ptr++ - '0'; + if (tm->tm_hour < 0 || tm->tm_hour > 23) break; + + /* Minute 00-59 */ + tm->tm_min = *ptr++ - '0'; + tm->tm_min *=10; tm->tm_min += *ptr++ - '0'; + if (tm->tm_min < 0 || tm->tm_min > 59) break; + + /* Second 00-61 */ + tm->tm_sec = *ptr++ - '0'; + tm->tm_sec *=10; tm->tm_sec += *ptr++ - '0'; + if (tm->tm_sec < 0 || tm->tm_sec > 61) break; + + /* Fractions of seconds */ + if ( *ptr == '.' ) { + ptr++; + for (i = 0, fracs = 0; isdigit((unsigned char) *ptr); ) { + i*=10; i+= *ptr++ - '0'; + fracs++; + } + tm->tm_nsec = i; + if (i) { + for (i = fracs; i<9; i++) + tm->tm_nsec *= 10; + } + } else { + tm->tm_nsec = 0; + } + tm->tm_usub = 0; + + /* Must be UTC */ + if (*ptr != 'Z') break; + + return 0; + } + return -1; +} + +/* strcopy is like strcpy except it returns a pointer to the trailing NUL of + * the result string. This allows fast construction of catenated strings + * without the overhead of strlen/strcat. + */ +char * +lutil_strcopy( + char *a, + const char *b +) +{ + if (!a || !b) + return a; + + while ((*a++ = *b++)) ; + return a-1; +} + +/* strncopy is like strcpy except it returns a pointer to the trailing NUL of + * the result string. This allows fast construction of catenated strings + * without the overhead of strlen/strcat. + */ +char * +lutil_strncopy( + char *a, + const char *b, + size_t n +) +{ + if (!a || !b || n == 0) + return a; + + while ((*a++ = *b++) && n-- > 0) ; + return a-1; +} + +/* memcopy is like memcpy except it returns a pointer to the byte past + * the end of the result buffer, set to NULL. This allows fast construction + * of catenated buffers. Provided for API consistency with lutil_str*copy(). + */ +char * +lutil_memcopy( + char *a, + const char *b, + size_t n +) +{ + AC_MEMCPY(a, b, n); + return a + n; +} + +#ifndef HAVE_MKSTEMP +int mkstemp( char * template ) +{ +#ifdef HAVE_MKTEMP + return open ( mktemp ( template ), O_RDWR|O_CREAT|O_EXCL, 0600 ); +#else + return -1; +#endif +} +#endif + +#ifdef _MSC_VER +/* Equivalent of MS CRT's _dosmaperr(). + * @param lastError[in] Result of GetLastError(). + */ +static errno_t win2errno(DWORD lastError) +{ + const struct { + DWORD windows_code; + errno_t errno_code; + } WIN2ERRNO_TABLE[] = { + { ERROR_SUCCESS, 0 }, + { ERROR_FILE_NOT_FOUND, ENOENT }, + { ERROR_PATH_NOT_FOUND, ENOENT }, + { ERROR_TOO_MANY_OPEN_FILES, EMFILE }, + { ERROR_ACCESS_DENIED, EACCES }, + { ERROR_INVALID_HANDLE, EBADF }, + { ERROR_NOT_ENOUGH_MEMORY, ENOMEM }, + { ERROR_LOCK_VIOLATION, EACCES }, + { ERROR_FILE_EXISTS, EEXIST }, + { ERROR_INVALID_PARAMETER, EINVAL }, + { ERROR_FILENAME_EXCED_RANGE, ENAMETOOLONG }, + }; + const unsigned int WIN2ERRNO_TABLE_SIZE = sizeof(WIN2ERRNO_TABLE) / +sizeof(WIN2ERRNO_TABLE[0]); + const errno_t DEFAULT_ERRNO_ERROR = -1; + unsigned int i; + + for (i = 0; i < WIN2ERRNO_TABLE_SIZE; ++i) { + if (WIN2ERRNO_TABLE[i].windows_code == lastError) { + return WIN2ERRNO_TABLE[i].errno_code; + } + } + return DEFAULT_ERRNO_ERROR; +} + +struct dirent { + char *d_name; +}; +typedef struct DIR { + HANDLE dir; + struct dirent data; + int first; + char buf[MAX_PATH+1]; +} DIR; +DIR *opendir( char *path ) +{ + char tmp[32768]; + int len = strlen(path); + DIR *d; + HANDLE h; + WIN32_FIND_DATA data; + + if (len+3 >= sizeof(tmp)) { + errno = ENAMETOOLONG; + return NULL; + } + + strcpy(tmp, path); + tmp[len++] = '\\'; + tmp[len++] = '*'; + tmp[len] = '\0'; + + h = FindFirstFile( tmp, &data ); + + if ( h == INVALID_HANDLE_VALUE ) { + errno = win2errno( GetLastError()); + return NULL; + } + + d = ber_memalloc( sizeof(DIR) ); + if ( !d ) + return NULL; + d->dir = h; + d->data.d_name = d->buf; + d->first = 1; + strcpy(d->data.d_name, data.cFileName); + return d; +} +struct dirent *readdir(DIR *dir) +{ + WIN32_FIND_DATA data; + + if (dir->first) { + dir->first = 0; + } else { + if (!FindNextFile(dir->dir, &data)) + return NULL; + strcpy(dir->data.d_name, data.cFileName); + } + return &dir->data; +} +int closedir(DIR *dir) +{ + (void) FindClose(dir->dir); + ber_memfree(dir); + return 0; +} +#endif + +/* + * Memory Reverse Search + */ +void * +(lutil_memrchr)(const void *b, int c, size_t n) +{ + if (n != 0) { + const unsigned char *s, *bb = b, cc = c; + + for ( s = bb + n; s > bb; ) { + if ( *--s == cc ) { + return (void *) s; + } + } + } + + return NULL; +} + +int +lutil_atoix( int *v, const char *s, int x ) +{ + char *next; + long i; + + assert( s != NULL ); + assert( v != NULL ); + + i = strtol( s, &next, x ); + if ( next == s || next[ 0 ] != '\0' ) { + return -1; + } + + if ( (long)(int)i != i ) { + return 1; + } + + *v = (int)i; + + return 0; +} + +int +lutil_atoux( unsigned *v, const char *s, int x ) +{ + char *next; + unsigned long u; + + assert( s != NULL ); + assert( v != NULL ); + + /* strtoul() has an odd interface */ + if ( s[ 0 ] == '-' ) { + return -1; + } + + u = strtoul( s, &next, x ); + if ( next == s || next[ 0 ] != '\0' ) { + return -1; + } + + if ( (unsigned long)(unsigned)u != u ) { + return 1; + } + + *v = u; + + return 0; +} + +int +lutil_atolx( long *v, const char *s, int x ) +{ + char *next; + long l; + int save_errno; + + assert( s != NULL ); + assert( v != NULL ); + + if ( isspace( s[ 0 ] ) ) { + return -1; + } + + errno = 0; + l = strtol( s, &next, x ); + save_errno = errno; + if ( next == s || next[ 0 ] != '\0' ) { + return -1; + } + + if ( ( l == LONG_MIN || l == LONG_MAX ) && save_errno != 0 ) { + return -1; + } + + *v = l; + + return 0; +} + +int +lutil_atoulx( unsigned long *v, const char *s, int x ) +{ + char *next; + unsigned long ul; + int save_errno; + + assert( s != NULL ); + assert( v != NULL ); + + /* strtoul() has an odd interface */ + if ( s[ 0 ] == '-' || isspace( s[ 0 ] ) ) { + return -1; + } + + errno = 0; + ul = strtoul( s, &next, x ); + save_errno = errno; + if ( next == s || next[ 0 ] != '\0' ) { + return -1; + } + + if ( ( ul == 0 || ul == ULONG_MAX ) && save_errno != 0 ) { + return -1; + } + + *v = ul; + + return 0; +} + +#ifdef HAVE_LONG_LONG +#if defined(HAVE_STRTOLL) || defined(HAVE_STRTOQ) +int +lutil_atollx( long long *v, const char *s, int x ) +{ + char *next; + long long ll; + int save_errno; + + assert( s != NULL ); + assert( v != NULL ); + + if ( isspace( s[ 0 ] ) ) { + return -1; + } + + errno = 0; +#ifdef HAVE_STRTOLL + ll = strtoll( s, &next, x ); +#else /* HAVE_STRTOQ */ + ll = (unsigned long long)strtoq( s, &next, x ); +#endif /* HAVE_STRTOQ */ + save_errno = errno; + if ( next == s || next[ 0 ] != '\0' ) { + return -1; + } + + /* LLONG_MIN, LLONG_MAX are C99 only */ +#if defined (LLONG_MIN) && defined(LLONG_MAX) + if ( ( ll == LLONG_MIN || ll == LLONG_MAX ) && save_errno != 0 ) { + return -1; + } +#endif /* LLONG_MIN && LLONG_MAX */ + + *v = ll; + + return 0; +} +#endif /* HAVE_STRTOLL || HAVE_STRTOQ */ + +#if defined(HAVE_STRTOULL) || defined(HAVE_STRTOUQ) +int +lutil_atoullx( unsigned long long *v, const char *s, int x ) +{ + char *next; + unsigned long long ull; + int save_errno; + + assert( s != NULL ); + assert( v != NULL ); + + /* strtoull() has an odd interface */ + if ( s[ 0 ] == '-' || isspace( s[ 0 ] ) ) { + return -1; + } + + errno = 0; +#ifdef HAVE_STRTOULL + ull = strtoull( s, &next, x ); +#else /* HAVE_STRTOUQ */ + ull = (unsigned long long)strtouq( s, &next, x ); +#endif /* HAVE_STRTOUQ */ + save_errno = errno; + if ( next == s || next[ 0 ] != '\0' ) { + return -1; + } + + /* ULLONG_MAX is C99 only */ +#if defined(ULLONG_MAX) + if ( ( ull == 0 || ull == ULLONG_MAX ) && save_errno != 0 ) { + return -1; + } +#endif /* ULLONG_MAX */ + + *v = ull; + + return 0; +} +#endif /* HAVE_STRTOULL || HAVE_STRTOUQ */ +#endif /* HAVE_LONG_LONG */ + +/* Multiply an integer by 100000000 and add new */ +typedef struct lutil_int_decnum { + unsigned char *buf; + int bufsiz; + int beg; + int len; +} lutil_int_decnum; + +#define FACTOR1 (100000000&0xffff) +#define FACTOR2 (100000000>>16) + +static void +scale( int new, lutil_int_decnum *prev, unsigned char *tmp ) +{ + int i, j; + unsigned char *in = prev->buf+prev->beg; + unsigned int part; + unsigned char *out = tmp + prev->bufsiz - prev->len; + + memset( tmp, 0, prev->bufsiz ); + if ( prev->len ) { + for ( i = prev->len-1; i>=0; i-- ) { + part = in[i] * FACTOR1; + for ( j = i; part; j-- ) { + part += out[j]; + out[j] = part & 0xff; + part >>= 8; + } + part = in[i] * FACTOR2; + for ( j = i-2; part; j-- ) { + part += out[j]; + out[j] = part & 0xff; + part >>= 8; + } + } + j++; + prev->beg += j; + prev->len -= j; + } + + out = tmp + prev->bufsiz; + i = 0; + do { + i--; + new += out[i]; + out[i] = new & 0xff; + new >>= 8; + } while ( new ); + i = -i; + if ( prev->len < i ) { + prev->beg = prev->bufsiz - i; + prev->len = i; + } + AC_MEMCPY( prev->buf+prev->beg, tmp+prev->beg, prev->len ); +} + +/* Convert unlimited length decimal or hex string to binary. + * Output buffer must be provided, bv_len must indicate buffer size + * Hex input can be "0x1234" or "'1234'H" + * + * Note: High bit of binary form is always the sign bit. If the number + * is supposed to be positive but has the high bit set, a zero byte + * is prepended. It is assumed that this has already been handled on + * any hex input. + */ +int +lutil_str2bin( struct berval *in, struct berval *out, void *ctx ) +{ + char *pin, *pout; + char *end; + int i, chunk, len, rc = 0, hex = 0; + if ( !out || !out->bv_val || out->bv_len < in->bv_len ) + return -1; + + pout = out->bv_val; + /* Leading "0x" for hex input */ + if ( in->bv_len > 2 && in->bv_val[0] == '0' && + ( in->bv_val[1] == 'x' || in->bv_val[1] == 'X' ) ) + { + len = in->bv_len - 2; + pin = in->bv_val + 2; + hex = 1; + } else if ( in->bv_len > 3 && in->bv_val[0] == '\'' && + in->bv_val[in->bv_len-2] == '\'' && + in->bv_val[in->bv_len-1] == 'H' ) + { + len = in->bv_len - 3; + pin = in->bv_val + 1; + hex = 1; + } + if ( hex ) { +#define HEXMAX (2 * sizeof(long)) + unsigned long l; + char tbuf[HEXMAX+1]; + + /* Convert a longword at a time, but handle leading + * odd bytes first + */ + chunk = len % HEXMAX; + if ( !chunk ) + chunk = HEXMAX; + + while ( len ) { + int ochunk; + memcpy( tbuf, pin, chunk ); + tbuf[chunk] = '\0'; + errno = 0; + l = strtoul( tbuf, &end, 16 ); + if ( errno ) + return -1; + ochunk = (chunk + 1)/2; + for ( i = ochunk - 1; i >= 0; i-- ) { + pout[i] = l & 0xff; + l >>= 8; + } + pin += chunk; + pout += ochunk; + len -= chunk; + chunk = HEXMAX; + } + out->bv_len = pout - out->bv_val; + } else { + /* Decimal */ +#define DECMAX 8 /* 8 digits at a time */ + char tmpbuf[64], *tmp; + lutil_int_decnum num; + int neg = 0; + long l; + char tbuf[DECMAX+1]; + + len = in->bv_len; + pin = in->bv_val; + num.buf = (unsigned char *)out->bv_val; + num.bufsiz = out->bv_len; + num.beg = num.bufsiz-1; + num.len = 0; + if ( pin[0] == '-' ) { + neg = 0xff; + len--; + pin++; + } + + /* tmp must be at least as large as outbuf */ + if ( out->bv_len > sizeof(tmpbuf)) { + tmp = ber_memalloc_x( out->bv_len, ctx ); + } else { + tmp = tmpbuf; + } + chunk = len & (DECMAX-1); + if ( !chunk ) + chunk = DECMAX; + + while ( len ) { + memcpy( tbuf, pin, chunk ); + tbuf[chunk] = '\0'; + errno = 0; + l = strtol( tbuf, &end, 10 ); + if ( errno ) { + rc = -1; + goto decfail; + } + scale( l, &num, (unsigned char *)tmp ); + pin += chunk; + len -= chunk; + chunk = DECMAX; + } + /* Negate the result */ + if ( neg ) { + unsigned char *ptr; + + ptr = num.buf+num.beg; + + /* flip all bits */ + for ( i=0; i<num.len; i++ ) + ptr[i] ^= 0xff; + + /* add 1, with carry - overflow handled below */ + while ( i-- && ! (ptr[i] = (ptr[i] + 1) & 0xff )) ; + } + /* Prepend sign byte if wrong sign bit */ + if (( num.buf[num.beg] ^ neg ) & 0x80 ) { + num.beg--; + num.len++; + num.buf[num.beg] = neg; + } + if ( num.beg ) + AC_MEMCPY( num.buf, num.buf+num.beg, num.len ); + out->bv_len = num.len; +decfail: + if ( tmp != tmpbuf ) { + ber_memfree_x( tmp, ctx ); + } + } + return rc; +} + +static char time_unit[] = "dhms"; + +/* Used to parse and unparse time intervals, not timestamps */ +int +lutil_parse_time( + const char *in, + unsigned long *tp ) +{ + unsigned long t = 0; + char *s, + *next; + int sofar = -1, + scale[] = { 86400, 3600, 60, 1 }; + + *tp = 0; + + for ( s = (char *)in; s[ 0 ] != '\0'; ) { + unsigned long u; + char *what; + + /* strtoul() has an odd interface */ + if ( s[ 0 ] == '-' ) { + return -1; + } + + u = strtoul( s, &next, 10 ); + if ( next == s ) { + return -1; + } + + if ( next[ 0 ] == '\0' ) { + /* assume seconds */ + t += u; + break; + } + + what = strchr( time_unit, next[ 0 ] ); + if ( what == NULL ) { + return -1; + } + + if ( what - time_unit <= sofar ) { + return -1; + } + + sofar = what - time_unit; + t += u * scale[ sofar ]; + + s = &next[ 1 ]; + } + + *tp = t; + return 0; +} + +int +lutil_unparse_time( + char *buf, + size_t buflen, + unsigned long t ) +{ + int len, i; + unsigned long v[ 4 ]; + char *ptr = buf; + + v[ 0 ] = t/86400; + v[ 1 ] = (t%86400)/3600; + v[ 2 ] = (t%3600)/60; + v[ 3 ] = t%60; + + for ( i = 0; i < 4; i++ ) { + if ( v[i] > 0 || ( i == 3 && ptr == buf ) ) { + len = snprintf( ptr, buflen, "%lu%c", v[ i ], time_unit[ i ] ); + if ( len < 0 || (unsigned)len >= buflen ) { + return -1; + } + buflen -= len; + ptr += len; + } + } + + return 0; +} + +/* + * formatted print to string + * + * - if return code < 0, the error code returned by vsnprintf(3) is returned + * + * - if return code > 0, the buffer was not long enough; + * - if next is not NULL, *next will be set to buf + bufsize - 1 + * - if len is not NULL, *len will contain the required buffer length + * + * - if return code == 0, the buffer was long enough; + * - if next is not NULL, *next will point to the end of the string printed so far + * - if len is not NULL, *len will contain the length of the string printed so far + */ +int +lutil_snprintf( char *buf, ber_len_t bufsize, char **next, ber_len_t *len, LDAP_CONST char *fmt, ... ) +{ + va_list ap; + int ret; + + assert( buf != NULL ); + assert( bufsize > 0 ); + assert( fmt != NULL ); + + va_start( ap, fmt ); + ret = vsnprintf( buf, bufsize, fmt, ap ); + va_end( ap ); + + if ( ret < 0 ) { + return ret; + } + + if ( len ) { + *len = ret; + } + + if ( (unsigned) ret >= bufsize ) { + if ( next ) { + *next = &buf[ bufsize - 1 ]; + } + + return 1; + } + + if ( next ) { + *next = &buf[ ret ]; + } + + return 0; +} diff --git a/libraries/liblutil/uuid.c b/libraries/liblutil/uuid.c new file mode 100644 index 0000000..061b2a2 --- /dev/null +++ b/libraries/liblutil/uuid.c @@ -0,0 +1,460 @@ +/* uuid.c -- Universally Unique Identifier routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2022 The OpenLDAP Foundation. + * Portions Copyright 2000-2003 Kurt D. Zeilenga. + * 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 + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright 2000, John E. Schimmel, All rights reserved. + * This software is not subject to any license of Mirapoint, Inc. + * + * This is free software; you can redistribute and use it + * under the same terms as OpenLDAP itself. + */ +/* This work was initially developed by John E. Schimmel and adapted + * for inclusion in OpenLDAP Software by Kurt D. Zeilenga. + */ + +/* + * Sorry this file is so scary, but it needs to run on a wide range of + * platforms. The only exported routine is lutil_uuidstr() which is all + * that LDAP cares about. It generates a new uuid and returns it in + * in string form. + */ +#include "portable.h" + +#include <limits.h> +#include <stdio.h> +#include <sys/types.h> + +#include <ac/stdlib.h> +#include <ac/string.h> /* get memcmp() */ + +#ifdef HAVE_UUID_TO_STR +# include <sys/uuid.h> +#elif defined( HAVE_UUID_GENERATE ) +# include <uuid/uuid.h> +#elif defined( _WIN32 ) +# include <rpc.h> +#else +# include <ac/socket.h> +# include <ac/time.h> +# ifdef HAVE_SYS_SYSCTL_H +# include <net/if.h> +# include <sys/sysctl.h> +# include <net/route.h> +# endif +#endif + +#include <lutil.h> + +/* not needed for Windows */ +#if !defined(HAVE_UUID_TO_STR) && !defined(HAVE_UUID_GENERATE) && !defined(_WIN32) +static unsigned char * +lutil_eaddr( void ) +{ + static unsigned char zero[6]; + static unsigned char eaddr[6]; + +#ifdef HAVE_SYS_SYSCTL_H + size_t needed; + int mib[6]; + char *buf, *next, *lim; + struct if_msghdr *ifm; + struct sockaddr_dl *sdl; + + if (memcmp(eaddr, zero, sizeof(eaddr))) { + return eaddr; + } + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[3] = 0; + mib[3] = 0; + mib[4] = NET_RT_IFLIST; + mib[5] = 0; + + if (sysctl(mib, sizeof(mib), NULL, &needed, NULL, 0) < 0) { + return NULL; + } + + buf = malloc(needed); + if( buf == NULL ) return NULL; + + if (sysctl(mib, sizeof(mib), buf, &needed, NULL, 0) < 0) { + free(buf); + return NULL; + } + + lim = buf + needed; + for (next = buf; next < lim; next += ifm->ifm_msglen) { + ifm = (struct if_msghdr *)next; + sdl = (struct sockaddr_dl *)(ifm + 1); + + if ( sdl->sdl_family != AF_LINK || sdl->sdl_alen == 6 ) { + AC_MEMCPY(eaddr, + (unsigned char *)sdl->sdl_data + sdl->sdl_nlen, + sizeof(eaddr)); + free(buf); + return eaddr; + } + } + + free(buf); + return NULL; + +#elif defined( SIOCGIFADDR ) && defined( AFLINK ) + char buf[sizeof(struct ifreq) * 32]; + struct ifconf ifc; + struct ifreq *ifr; + struct sockaddr *sa; + struct sockaddr_dl *sdl; + unsigned char *p; + int s, i; + + if (memcmp(eaddr, zero, sizeof(eaddr))) { + return eaddr; + } + + s = socket( AF_INET, SOCK_DGRAM, 0 ); + if ( s < 0 ) { + return NULL; + } + + ifc.ifc_len = sizeof( buf ); + ifc.ifc_buf = buf; + memset( buf, 0, sizeof( buf ) ); + + i = ioctl( s, SIOCGIFCONF, (char *)&ifc ); + close( s ); + + if( i < 0 ) { + return NULL; + } + + for ( i = 0; i < ifc.ifc_len; ) { + ifr = (struct ifreq *)&ifc.ifc_buf[i]; + sa = &ifr->ifr_addr; + + if ( sa->sa_len > sizeof( ifr->ifr_addr ) ) { + i += sizeof( ifr->ifr_name ) + sa->sa_len; + } else { + i += sizeof( *ifr ); + } + + if ( sa->sa_family != AF_LINK ) { + continue; + } + + sdl = (struct sockaddr_dl *)sa; + + if ( sdl->sdl_alen == 6 ) { + AC_MEMCPY(eaddr, + (unsigned char *)sdl->sdl_data + sdl->sdl_nlen, + sizeof(eaddr)); + return eaddr; + } + } + + return NULL; + +#else + if (memcmp(eaddr, zero, sizeof(eaddr)) == 0) { + /* XXX - who knows? */ + lutil_entropy( eaddr, sizeof(eaddr) ); + eaddr[0] |= 0x01; /* turn it into a multicast address */ + } + + return eaddr; +#endif +} + +#if (ULONG_MAX >> 31 >> 31) > 1 || defined HAVE_LONG_LONG + +#if (ULONG_MAX >> 31 >> 31) > 1 + typedef unsigned long UI64; + /* 100 usec intervals from 10/10/1582 to 1/1/1970 */ +# define UUID_TPLUS 0x01B21DD2138140ul +#else + typedef unsigned long long UI64; +# define UUID_TPLUS 0x01B21DD2138140ull +#endif + +#define high32(i) ((unsigned long) ((i) >> 32)) +#define low32(i) ((unsigned long) (i) & 0xFFFFFFFFul) +#define set_add64(res, i) ((res) += (i)) +#define set_add64l(res, i) ((res) += (i)) +#define mul64ll(i1, i2) ((UI64) (i1) * (i2)) + +#else /* ! (ULONG_MAX >= 64 bits || HAVE_LONG_LONG) */ + +typedef struct { + unsigned long high, low; +} UI64; + +static const UI64 UUID_TPLUS = { 0x01B21Dul, 0xD2138140ul }; + +#define high32(i) ((i).high) +#define low32(i) ((i).low) + +/* res += ui64 */ +#define set_add64(res, ui64) \ +{ \ + res.high += ui64.high; \ + res.low = (res.low + ui64.low) & 0xFFFFFFFFul; \ + if (res.low < ui64.low) res.high++; \ +} + +/* res += ul32 */ +#define set_add64l(res, ul32) \ +{ \ + res.low = (res.low + ul32) & 0xFFFFFFFFul; \ + if (res.low < ul32) res.high++; \ +} + +/* compute i1 * i2 */ +static UI64 +mul64ll(unsigned long i1, unsigned long i2) +{ + const unsigned int high1 = (i1 >> 16), low1 = (i1 & 0xffff); + const unsigned int high2 = (i2 >> 16), low2 = (i2 & 0xffff); + + UI64 res; + unsigned long tmp; + + res.high = (unsigned long) high1 * high2; + res.low = (unsigned long) low1 * low2; + + tmp = (unsigned long) low1 * high2; + res.high += (tmp >> 16); + tmp = (tmp << 16) & 0xFFFFFFFFul; + res.low = (res.low + tmp) & 0xFFFFFFFFul; + if (res.low < tmp) + res.high++; + + tmp = (unsigned long) low2 * high1; + res.high += (tmp >> 16); + tmp = (tmp << 16) & 0xFFFFFFFFul; + res.low = (res.low + tmp) & 0xFFFFFFFFul; + if (res.low < tmp) + res.high++; + + return res; +} + +#endif /* ULONG_MAX >= 64 bits || HAVE_LONG_LONG */ + +#endif /* !HAVE_UUID_TO_STR && !HAVE_UUID_GENERATE && !_WIN32 */ + +/* +** All we really care about is an ISO UUID string. The format of a UUID is: +** field octet note +** time_low 0-3 low field of the timestamp +** time_mid 4-5 middle field of timestamp +** time_hi_and_version 6-7 high field of timestamp and +** version number +** clock_seq_hi_and_resv 8 high field of clock sequence +** and variant +** clock_seq_low 9 low field of clock sequence +** node 10-15 spacially unique identifier +** +** We use DCE version one, and the DCE variant. Our unique identifier is +** the first ethernet address on the system. +*/ +size_t +lutil_uuidstr( char *buf, size_t len ) +{ +#ifdef HAVE_UUID_TO_STR + uuid_t uu = {0}; + unsigned rc; + char *s; + size_t l; + + uuid_create( &uu, &rc ); + if ( rc != uuid_s_ok ) { + return 0; + } + + uuid_to_str( &uu, &s, &rc ); + if ( rc != uuid_s_ok ) { + return 0; + } + + l = strlen( s ); + if ( l >= len ) { + free( s ); + return 0; + } + + strncpy( buf, s, len ); + free( s ); + + return l; + +#elif defined( HAVE_UUID_GENERATE ) + uuid_t uu; + + uuid_generate( uu ); + uuid_unparse_lower( uu, buf ); + return strlen( buf ); + +#elif defined( _WIN32 ) + UUID uuid; + unsigned char *uuidstr; + size_t uuidlen; + + if( UuidCreate( &uuid ) != RPC_S_OK ) { + return 0; + } + + if( UuidToString( &uuid, &uuidstr ) != RPC_S_OK ) { + return 0; + } + + uuidlen = strlen( uuidstr ); + if( uuidlen >= len ) { + return 0; + } + + strncpy( buf, uuidstr, len ); + RpcStringFree( &uuidstr ); + + return uuidlen; + +#else + struct timeval tv; + UI64 tl; + unsigned char *nl; + unsigned short t2, t3, s1; + unsigned long t1, tl_high; + unsigned int rc; + + /* + * Theoretically we should delay if seq wraps within 100usec but for now + * systems are not fast enough to worry about it. + */ + static int inited = 0; + static unsigned short seq; + + if (!inited) { + lutil_entropy( (unsigned char *) &seq, sizeof(seq) ); + inited++; + } + +#ifdef HAVE_GETTIMEOFDAY + gettimeofday( &tv, 0 ); +#else + time( &tv.tv_sec ); + tv.tv_usec = 0; +#endif + + tl = mul64ll(tv.tv_sec, 10000000UL); + set_add64l(tl, tv.tv_usec * 10UL); + set_add64(tl, UUID_TPLUS); + + nl = lutil_eaddr(); + + t1 = low32(tl); /* time_low */ + tl_high = high32(tl); + t2 = tl_high & 0xffff; /* time_mid */ + t3 = ((tl_high >> 16) & 0x0fff) | 0x1000; /* time_hi_and_version */ + s1 = ( ++seq & 0x1fff ) | 0x8000; /* clock_seq_and_reserved */ + + rc = snprintf( buf, len, + "%08lx-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", + t1, (unsigned) t2, (unsigned) t3, (unsigned) s1, + (unsigned) nl[0], (unsigned) nl[1], + (unsigned) nl[2], (unsigned) nl[3], + (unsigned) nl[4], (unsigned) nl[5] ); + + return rc < len ? rc : 0; +#endif +} + +int +lutil_uuidstr_from_normalized( + char *uuid, + size_t uuidlen, + char *buf, + size_t buflen ) +{ + unsigned char nibble; + int i, d = 0; + + assert( uuid != NULL ); + assert( buf != NULL ); + + if ( uuidlen != 16 ) return -1; + if ( buflen < 36 ) return -1; + + for ( i = 0; i < 16; i++ ) { + if ( i == 4 || i == 6 || i == 8 || i == 10 ) { + buf[(i<<1)+d] = '-'; + d += 1; + } + + nibble = (uuid[i] >> 4) & 0xF; + if ( nibble < 10 ) { + buf[(i<<1)+d] = nibble + '0'; + } else { + buf[(i<<1)+d] = nibble - 10 + 'a'; + } + + nibble = (uuid[i]) & 0xF; + if ( nibble < 10 ) { + buf[(i<<1)+d+1] = nibble + '0'; + } else { + buf[(i<<1)+d+1] = nibble - 10 + 'a'; + } + } + + if ( buflen > 36 ) buf[36] = '\0'; + return 36; +} + +#ifdef TEST +int +main(int argc, char **argv) +{ + char buf1[8], buf2[64]; + +#ifndef HAVE_UUID_TO_STR + unsigned char *p = lutil_eaddr(); + + if( p ) { + printf( "Ethernet Address: %02x:%02x:%02x:%02x:%02x:%02x\n", + (unsigned) p[0], (unsigned) p[1], (unsigned) p[2], + (unsigned) p[3], (unsigned) p[4], (unsigned) p[5]); + } +#endif + + if ( lutil_uuidstr( buf1, sizeof( buf1 ) ) ) { + printf( "UUID: %s\n", buf1 ); + } else { + fprintf( stderr, "too short: %ld\n", (long) sizeof( buf1 ) ); + } + + if ( lutil_uuidstr( buf2, sizeof( buf2 ) ) ) { + printf( "UUID: %s\n", buf2 ); + } else { + fprintf( stderr, "too short: %ld\n", (long) sizeof( buf2 ) ); + } + + if ( lutil_uuidstr( buf2, sizeof( buf2 ) ) ) { + printf( "UUID: %s\n", buf2 ); + } else { + fprintf( stderr, "too short: %ld\n", (long) sizeof( buf2 ) ); + } + + return 0; +} +#endif |