diff options
Diffstat (limited to 'clients/tools')
-rw-r--r-- | clients/tools/Makefile.in | 129 | ||||
-rw-r--r-- | clients/tools/common.c | 2444 | ||||
-rw-r--r-- | clients/tools/common.h | 141 | ||||
-rw-r--r-- | clients/tools/ldapcompare.c | 366 | ||||
-rw-r--r-- | clients/tools/ldapdelete.c | 443 | ||||
-rw-r--r-- | clients/tools/ldapexop.c | 355 | ||||
-rw-r--r-- | clients/tools/ldapmodify.c | 1282 | ||||
-rw-r--r-- | clients/tools/ldapmodrdn.c | 330 | ||||
-rw-r--r-- | clients/tools/ldappasswd.c | 413 | ||||
-rw-r--r-- | clients/tools/ldapsearch.c | 1926 | ||||
-rw-r--r-- | clients/tools/ldapurl.c | 305 | ||||
-rw-r--r-- | clients/tools/ldapwhoami.c | 235 |
12 files changed, 8369 insertions, 0 deletions
diff --git a/clients/tools/Makefile.in b/clients/tools/Makefile.in new file mode 100644 index 0000000..691d54c --- /dev/null +++ b/clients/tools/Makefile.in @@ -0,0 +1,129 @@ +# Makefile for LDAP tools +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 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>. + +SRCS = ldapsearch.c ldapmodify.c ldapdelete.c ldapmodrdn.c \ + ldappasswd.c ldapwhoami.c ldapcompare.c \ + ldapexop.c ldapurl.c common.c +OBJS = ldapsearch.o ldapmodify.o ldapdelete.o ldapmodrdn.o \ + ldappasswd.o ldapwhoami.o ldapcompare.o \ + ldapexop.o ldapurl.o common.o + +LDAP_INCDIR= ../../include +LDAP_LIBDIR= ../../libraries + +MKVOPTS = -s + +XLIBS = $(LDAP_L) +XXLIBS = $(SECURITY_LIBS) $(LUTIL_LIBS) + +XSRCS = ldsversion.c ldmversion.c lddversion.c ldrversion.c \ + ldpversion.c ldwversion.c ldcversion.c ldeversion.c lduversion.c + +PROGRAMS = ldapsearch ldapmodify ldapdelete ldapmodrdn \ + ldappasswd ldapwhoami ldapcompare ldapexop ldapurl + + +ldapsearch: ldsversion.o + $(LTLINK) -o $@ ldapsearch.o common.o ldsversion.o $(LIBS) + +ldapmodify: ldmversion.o + $(LTLINK) -o $@ ldapmodify.o common.o ldmversion.o $(LIBS) + +ldapdelete: lddversion.o + $(LTLINK) -o $@ ldapdelete.o common.o lddversion.o $(LIBS) + +ldapmodrdn: ldrversion.o + $(LTLINK) -o $@ ldapmodrdn.o common.o ldrversion.o $(LIBS) + +ldappasswd: ldpversion.o + $(LTLINK) -o $@ ldappasswd.o common.o ldpversion.o $(LIBS) + +ldapwhoami: ldwversion.o + $(LTLINK) -o $@ ldapwhoami.o common.o ldwversion.o $(LIBS) + +ldapcompare: ldcversion.o + $(LTLINK) -o $@ ldapcompare.o common.o ldcversion.o $(LIBS) + +ldapexop: ldeversion.o + $(LTLINK) -o $@ ldapexop.o common.o ldeversion.o $(LIBS) + +ldapurl: lduversion.o + $(LTLINK) -o $@ ldapurl.o lduversion.o $(LIBS) + +ldsversion.c: Makefile + @-$(RM) $@ + $(MKVERSION) $(MKVOPTS) ldapsearch > $@ + +ldsversion.o: ldapsearch.o common.o $(XLIBS) + +ldmversion.c: Makefile + @-$(RM) $@ + $(MKVERSION) $(MKVOPTS) ldapmodify > $@ + +ldmversion.o: ldapmodify.o common.o $(XLIBS) + +lddversion.c: Makefile + @-$(RM) $@ + $(MKVERSION) $(MKVOPTS) ldapdelete > $@ + +lddversion.o: ldapdelete.o common.o $(XLIBS) + +ldpversion.c: Makefile + @-$(RM) $@ + $(MKVERSION) $(MKVOPTS) ldappasswd > $@ + +ldpversion.o: ldappasswd.o common.o $(XLIBS) + +ldrversion.c: Makefile + @-$(RM) $@ + $(MKVERSION) $(MKVOPTS) ldapmodrdn > $@ + +ldrversion.o: ldapmodrdn.o common.o $(XLIBS) + +ldwversion.c: Makefile + @-$(RM) $@ + $(MKVERSION) $(MKVOPTS) ldapwhoami > $@ + +ldwversion.o: ldapwhoami.o common.o $(XLIBS) + +ldcversion.c: Makefile + @-$(RM) $@ + $(MKVERSION) $(MKVOPTS) ldapcompare > $@ + +ldcversion.o: ldapcompare.o common.o $(XLIBS) + +ldeversion.c: Makefile + @-$(RM) $@ + $(MKVERSION) $(MKVOPTS) ldapexop > $@ + +ldeversion.o: ldapexop.o common.o $(XLIBS) + +lduversion.c: Makefile + @-$(RM) $@ + $(MKVERSION) $(MKVOPTS) ldapurl > $@ + +lduversion.o: ldapurl.o $(XLIBS) + +install-local: FORCE + -$(MKDIR) $(DESTDIR)$(bindir) + @( \ + for prg in $(PROGRAMS); do \ + $(LTINSTALL) $(INSTALLFLAGS) $(STRIP) -m 755 $$prg$(EXEEXT) \ + $(DESTDIR)$(bindir); \ + done \ + ) + $(RM) $(DESTDIR)$(bindir)/ldapadd$(EXEEXT) + $(LN_S) $(DESTDIR)$(bindir)/ldapmodify$(EXEEXT) $(DESTDIR)$(bindir)/ldapadd$(EXEEXT) + diff --git a/clients/tools/common.c b/clients/tools/common.c new file mode 100644 index 0000000..39db70b --- /dev/null +++ b/clients/tools/common.c @@ -0,0 +1,2444 @@ +/* common.c - common routines for the ldap client tools */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * Portions Copyright 2003 Kurt D. Zeilenga. + * Portions Copyright 2003 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>. + */ +/* ACKNOWLEDGEMENTS: + * This file was initially created by Hallvard B. Furuseth based (in + * part) upon argument parsing code for individual tools located in + * this directory. Additional contributors include: + * Kurt D. Zeilenga (additional common argument and control support) + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/signal.h> +#include <ac/string.h> +#include <ac/ctype.h> +#include <ac/unistd.h> +#include <ac/errno.h> +#include <ac/time.h> +#include <ac/socket.h> + +#ifdef HAVE_CYRUS_SASL +#ifdef HAVE_SASL_SASL_H +#include <sasl/sasl.h> +#else +#include <sasl.h> +#endif +#endif + +#include <ldap.h> + +#include "ldif.h" +#include "lutil.h" +#include "lutil_ldap.h" +#include "ldap_defaults.h" +#include "ldap_pvt.h" +#include "lber_pvt.h" + +#include "common.h" + +/* input-related vars */ + +/* misc. parameters */ +tool_type_t tool_type; +int contoper = 0; +int debug = 0; +char *infile = NULL; +int dont = 0; +int nocanon = 0; +int referrals = 0; +int verbose = 0; +int ldif = 0; +ber_len_t ldif_wrap = 0; +char *prog = NULL; + +/* connection */ +char *ldapuri = NULL; +char *ldaphost = NULL; +int ldapport = 0; +int use_tls = 0; +int protocol = -1; +int version = 0; + +/* authc/authz */ +int authmethod = -1; +char *binddn = NULL; +int want_bindpw = 0; +struct berval passwd = { 0, NULL }; +char *pw_file = NULL; +#ifdef HAVE_CYRUS_SASL +unsigned sasl_flags = LDAP_SASL_AUTOMATIC; +char *sasl_realm = NULL; +char *sasl_authc_id = NULL; +char *sasl_authz_id = NULL; +char *sasl_mech = NULL; +char *sasl_secprops = NULL; +#endif + +/* controls */ +int assertctl; +char *assertion = NULL; +struct berval assertionvalue = BER_BVNULL; +char *authzid = NULL; +/* support deprecated early version of proxyAuthz */ +#define LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ "2.16.840.1.113730.3.4.12" +#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ +char *proxydn = NULL; +#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */ +int manageDIT = 0; +int manageDSAit = 0; +int noop = 0; +int ppolicy = 0; +int preread = 0; +static char *preread_attrs = NULL; +int postread = 0; +static char *postread_attrs = NULL; +ber_int_t pr_morePagedResults = 1; +struct berval pr_cookie = { 0, NULL }; +#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR +int chaining = 0; +static int chainingResolve = -1; +static int chainingContinuation = -1; +#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ +#ifdef LDAP_CONTROL_X_SESSION_TRACKING +static int sessionTracking = 0; +struct berval stValue; +#endif /* LDAP_CONTROL_X_SESSION_TRACKING */ +ber_int_t vlvPos; +ber_int_t vlvCount; +struct berval *vlvContext; + +LDAPControl *unknown_ctrls = NULL; +int unknown_ctrls_num = 0; + +/* options */ +struct timeval nettimeout = { -1 , 0 }; + +typedef int (*print_ctrl_fn)( LDAP *ld, LDAPControl *ctrl ); + +static int print_preread( LDAP *ld, LDAPControl *ctrl ); +static int print_postread( LDAP *ld, LDAPControl *ctrl ); +static int print_paged_results( LDAP *ld, LDAPControl *ctrl ); +#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST +static int print_ppolicy( LDAP *ld, LDAPControl *ctrl ); +#endif +static int print_sss( LDAP *ld, LDAPControl *ctrl ); +static int print_vlv( LDAP *ld, LDAPControl *ctrl ); +#ifdef LDAP_CONTROL_X_DEREF +static int print_deref( LDAP *ld, LDAPControl *ctrl ); +#endif +#ifdef LDAP_CONTROL_X_WHATFAILED +static int print_whatfailed( LDAP *ld, LDAPControl *ctrl ); +#endif +#ifdef LDAP_CONTROL_X_PASSWORD_EXPIRED +static int print_netscape_pwexpired( LDAP *ld, LDAPControl *ctrl ); +static int print_netscape_pwexpiring( LDAP *ld, LDAPControl *ctrl ); +#endif + +static struct tool_ctrls_t { + const char *oid; + unsigned mask; + print_ctrl_fn func; +} tool_ctrl_response[] = { + { LDAP_CONTROL_PRE_READ, TOOL_ALL, print_preread }, + { LDAP_CONTROL_POST_READ, TOOL_ALL, print_postread }, + { LDAP_CONTROL_PAGEDRESULTS, TOOL_SEARCH, print_paged_results }, +#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST + { LDAP_CONTROL_PASSWORDPOLICYRESPONSE, TOOL_ALL, print_ppolicy }, +#endif + { LDAP_CONTROL_SORTRESPONSE, TOOL_SEARCH, print_sss }, + { LDAP_CONTROL_VLVRESPONSE, TOOL_SEARCH, print_vlv }, +#ifdef LDAP_CONTROL_X_DEREF + { LDAP_CONTROL_X_DEREF, TOOL_SEARCH, print_deref }, +#endif +#ifdef LDAP_CONTROL_X_WHATFAILED + { LDAP_CONTROL_X_WHATFAILED, TOOL_ALL, print_whatfailed }, +#endif +#ifdef LDAP_CONTROL_X_PASSWORD_EXPIRED + { LDAP_CONTROL_X_PASSWORD_EXPIRED, TOOL_ALL, print_netscape_pwexpired }, + { LDAP_CONTROL_X_PASSWORD_EXPIRING, TOOL_ALL, print_netscape_pwexpiring }, +#endif + { NULL, 0, NULL } +}; + +/* "features" */ +enum { Intr_None = 0, Intr_Abandon, Intr_Cancel, Intr_Ignore }; +static volatile sig_atomic_t gotintr, abcan; + +int backlog; + + +#ifdef LDAP_CONTROL_X_SESSION_TRACKING +static int +st_value( LDAP *ld, struct berval *value ) +{ + char *ip = NULL, *name = NULL; + struct berval id = { 0 }; + char namebuf[ MAXHOSTNAMELEN ]; + + if ( gethostname( namebuf, sizeof( namebuf ) ) == 0 ) { + struct hostent *h; + struct in_addr addr; + + name = namebuf; + + h = gethostbyname( name ); + if ( h != NULL ) { + AC_MEMCPY( &addr, h->h_addr, sizeof( addr ) ); + ip = inet_ntoa( addr ); + } + } + +#ifdef HAVE_CYRUS_SASL + if ( sasl_authz_id != NULL ) { + ber_str2bv( sasl_authz_id, 0, 0, &id ); + + } else if ( sasl_authc_id != NULL ) { + ber_str2bv( sasl_authc_id, 0, 0, &id ); + + } else +#endif /* HAVE_CYRUS_SASL */ + if ( binddn != NULL ) { + ber_str2bv( binddn, 0, 0, &id ); + } + + if ( ldap_create_session_tracking_value( ld, + ip, name, LDAP_CONTROL_X_SESSION_TRACKING_USERNAME, + &id, &stValue ) ) + { + fprintf( stderr, _("Session tracking control encoding error!\n") ); + return -1; + } + + return 0; +} +#endif /* LDAP_CONTROL_X_SESSION_TRACKING */ + +RETSIGTYPE +do_sig( int sig ) +{ + gotintr = abcan; +} + +void +tool_init( tool_type_t type ) +{ + tool_type = type; + ldap_pvt_setlocale(LC_MESSAGES, ""); + ldap_pvt_bindtextdomain(OPENLDAP_PACKAGE, LDAP_LOCALEDIR); + ldap_pvt_textdomain(OPENLDAP_PACKAGE); +} + +void +tool_destroy( void ) +{ + static int destroyed; + if ( destroyed++ ) + return; + +#ifdef HAVE_CYRUS_SASL + sasl_done(); +#endif +#ifdef HAVE_TLS + ldap_pvt_tls_destroy(); +#endif + + if ( ldapuri != NULL ) { + ber_memfree( ldapuri ); + ldapuri = NULL; + } + + if ( pr_cookie.bv_val != NULL ) { + ber_memfree( pr_cookie.bv_val ); + BER_BVZERO( &pr_cookie ); + } + + if ( binddn != NULL ) { + ber_memfree( binddn ); + binddn = NULL; + } + + if ( passwd.bv_val != NULL ) { + ber_memfree( passwd.bv_val ); + BER_BVZERO( &passwd ); + } + + if ( infile != NULL ) { + ber_memfree( infile ); + infile = NULL; + } + + if ( assertion ) { + ber_memfree( assertion ); + assertion = NULL; + } + + if ( authzid ) { + ber_memfree( authzid ); + authzid = NULL; + } + + if ( proxydn ) { + ber_memfree( proxydn ); + proxydn = NULL; + } + + if ( preread_attrs ) { + ber_memfree( preread_attrs ); + preread_attrs = NULL; + } + + if ( postread_attrs ) { + ber_memfree( postread_attrs ); + postread_attrs = NULL; + } +} + +void +tool_common_usage( void ) +{ + static const char *const descriptions[] = { +N_(" -d level set LDAP debugging level to `level'\n"), +N_(" -D binddn bind DN\n"), +N_(" -e [!]<ext>[=<extparam>] general extensions (! indicates criticality)\n") +N_(" [!]assert=<filter> (RFC 4528; a RFC 4515 Filter string)\n") +N_(" [!]authzid=<authzid> (RFC 4370; \"dn:<dn>\" or \"u:<user>\")\n") +#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ +#if 0 + /* non-advertized support for proxyDN */ +N_(" [!]proxydn=<dn> (a RFC 4514 DN string)\n") +#endif +#endif +#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR +N_(" [!]chaining[=<resolveBehavior>[/<continuationBehavior>]]\n") +N_(" one of \"chainingPreferred\", \"chainingRequired\",\n") +N_(" \"referralsPreferred\", \"referralsRequired\"\n") +#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ +N_(" [!]manageDSAit (RFC 3296)\n") +N_(" [!]noop\n") +#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST +N_(" ppolicy\n") +#endif +N_(" [!]postread[=<attrs>] (RFC 4527; comma-separated attr list)\n") +N_(" [!]preread[=<attrs>] (RFC 4527; comma-separated attr list)\n") +N_(" [!]relax\n") +#ifdef LDAP_CONTROL_X_SESSION_TRACKING +N_(" [!]sessiontracking\n") +#endif /* LDAP_CONTROL_X_SESSION_TRACKING */ +N_(" abandon, cancel, ignore (SIGINT sends abandon/cancel,\n" + " or ignores response; if critical, doesn't wait for SIGINT.\n" + " not really controls)\n") +N_(" -h host LDAP server\n"), +N_(" -H URI LDAP Uniform Resource Identifier(s)\n"), +N_(" -I use SASL Interactive mode\n"), +N_(" -n show what would be done but don't actually do it\n"), +N_(" -N do not use reverse DNS to canonicalize SASL host name\n"), +N_(" -O props SASL security properties\n"), +N_(" -o <opt>[=<optparam>] general options\n"), +N_(" nettimeout=<timeout> (in seconds, or \"none\" or \"max\")\n"), +N_(" ldif-wrap=<width> (in columns, or \"no\" for no wrapping)\n"), +N_(" -p port port on LDAP server\n"), +N_(" -Q use SASL Quiet mode\n"), +N_(" -R realm SASL realm\n"), +N_(" -U authcid SASL authentication identity\n"), +N_(" -v run in verbose mode (diagnostics to standard output)\n"), +N_(" -V print version info (-VV only)\n"), +N_(" -w passwd bind password (for simple authentication)\n"), +N_(" -W prompt for bind password\n"), +N_(" -x Simple authentication\n"), +N_(" -X authzid SASL authorization identity (\"dn:<dn>\" or \"u:<user>\")\n"), +N_(" -y file Read password from file\n"), +N_(" -Y mech SASL mechanism\n"), +N_(" -Z Start TLS request (-ZZ to require successful response)\n"), +NULL + }; + const char *const *cpp; + + fputs( _("Common options:\n"), stderr ); + for( cpp = descriptions; *cpp != NULL; cpp++ ) { + if( strchr( options, (*cpp)[3] ) || (*cpp)[3] == ' ' ) { + fputs( _(*cpp), stderr ); + } + } + + tool_destroy(); +} + +void tool_perror( + const char *func, + int err, + const char *extra, + const char *matched, + const char *info, + char **refs ) +{ + fprintf( stderr, "%s: %s (%d)%s\n", + func, ldap_err2string( err ), err, extra ? extra : "" ); + + if ( matched && *matched ) { + fprintf( stderr, _("\tmatched DN: %s\n"), matched ); + } + + if ( info && *info ) { + fprintf( stderr, _("\tadditional info: %s\n"), info ); + } + + if ( refs && *refs ) { + int i; + fprintf( stderr, _("\treferrals:\n") ); + for( i=0; refs[i]; i++ ) { + fprintf( stderr, "\t\t%s\n", refs[i] ); + } + } +} + + +void +tool_args( int argc, char **argv ) +{ + int i; + + while (( i = getopt( argc, argv, options )) != EOF ) { + int crit, ival; + char *control, *cvalue, *next; + switch( i ) { + case 'c': /* continuous operation mode */ + contoper++; + break; + case 'C': /* referrals: obsolete */ + referrals++; + break; + case 'd': + ival = strtol( optarg, &next, 10 ); + if (next == NULL || next[0] != '\0') { + fprintf( stderr, "%s: unable to parse debug value \"%s\"\n", prog, optarg); + exit(EXIT_FAILURE); + } + debug |= ival; + break; + case 'D': /* bind DN */ + if( binddn != NULL ) { + fprintf( stderr, "%s: -D previously specified\n", prog ); + exit( EXIT_FAILURE ); + } + binddn = ber_strdup( optarg ); + break; + case 'e': /* general extensions (controls and such) */ + /* should be extended to support comma separated list of + * [!]key[=value] parameters, e.g. -e !foo,bar=567 + */ + + crit = 0; + cvalue = NULL; + if( optarg[0] == '!' ) { + crit = 1; + optarg++; + } + + control = ber_strdup( optarg ); + if ( (cvalue = strchr( control, '=' )) != NULL ) { + *cvalue++ = '\0'; + } + + if ( strcasecmp( control, "assert" ) == 0 ) { + if( assertctl ) { + fprintf( stderr, "assert control previously specified\n"); + exit( EXIT_FAILURE ); + } + if( cvalue == NULL ) { + fprintf( stderr, "assert: control value expected\n" ); + usage(); + } + + assertctl = 1 + crit; + + assert( assertion == NULL ); + assertion = ber_strdup( cvalue ); + + } else if ( strcasecmp( control, "authzid" ) == 0 ) { + if( authzid != NULL ) { + fprintf( stderr, "authzid control previously specified\n"); + exit( EXIT_FAILURE ); + } +#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ + if( proxydn != NULL ) { + fprintf( stderr, "authzid control incompatible with proxydn\n"); + exit( EXIT_FAILURE ); + } +#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */ + if( cvalue == NULL ) { + fprintf( stderr, "authzid: control value expected\n" ); + usage(); + } + if( !crit ) { + fprintf( stderr, "authzid: must be marked critical\n" ); + usage(); + } + + assert( authzid == NULL ); + authzid = ber_strdup( cvalue ); + +#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ + } else if ( strcasecmp( control, "proxydn" ) == 0 ) { + if( proxydn != NULL ) { + fprintf( stderr, "proxydn control previously specified\n"); + exit( EXIT_FAILURE ); + } + if( authzid != NULL ) { + fprintf( stderr, "proxydn control incompatible with authzid\n"); + exit( EXIT_FAILURE ); + } + if( cvalue == NULL ) { + fprintf( stderr, "proxydn: control value expected\n" ); + usage(); + } + if( !crit ) { + fprintf( stderr, "proxydn: must be marked critical\n" ); + usage(); + } + + assert( proxydn == NULL ); + proxydn = ber_strdup( cvalue ); +#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */ + + } else if ( ( strcasecmp( control, "relax" ) == 0 ) || + ( strcasecmp( control, "manageDIT" ) == 0 ) ) + { + if( manageDIT ) { + fprintf( stderr, + "relax control previously specified\n"); + exit( EXIT_FAILURE ); + } + if( cvalue != NULL ) { + fprintf( stderr, + "relax: no control value expected\n" ); + usage(); + } + + manageDIT = 1 + crit; + + } else if ( strcasecmp( control, "manageDSAit" ) == 0 ) { + if( manageDSAit ) { + fprintf( stderr, + "manageDSAit control previously specified\n"); + exit( EXIT_FAILURE ); + } + if( cvalue != NULL ) { + fprintf( stderr, + "manageDSAit: no control value expected\n" ); + usage(); + } + + manageDSAit = 1 + crit; + + } else if ( strcasecmp( control, "noop" ) == 0 ) { + if( noop ) { + fprintf( stderr, "noop control previously specified\n"); + exit( EXIT_FAILURE ); + } + if( cvalue != NULL ) { + fprintf( stderr, "noop: no control value expected\n" ); + usage(); + } + + noop = 1 + crit; + +#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST + } else if ( strcasecmp( control, "ppolicy" ) == 0 ) { + if( ppolicy ) { + fprintf( stderr, "ppolicy control previously specified\n"); + exit( EXIT_FAILURE ); + } + if( cvalue != NULL ) { + fprintf( stderr, "ppolicy: no control value expected\n" ); + usage(); + } + if( crit ) { + fprintf( stderr, "ppolicy: critical flag not allowed\n" ); + usage(); + } + + ppolicy = 1; +#endif + + } else if ( strcasecmp( control, "preread" ) == 0 ) { + if( preread ) { + fprintf( stderr, "preread control previously specified\n"); + exit( EXIT_FAILURE ); + } + + preread = 1 + crit; + preread_attrs = ber_strdup( cvalue ); + + } else if ( strcasecmp( control, "postread" ) == 0 ) { + if( postread ) { + fprintf( stderr, "postread control previously specified\n"); + exit( EXIT_FAILURE ); + } + + postread = 1 + crit; + postread_attrs = ber_strdup( cvalue ); + +#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR + } else if ( strcasecmp( control, "chaining" ) == 0 ) { + chaining = 1 + crit; + + if ( cvalue != NULL ) { + char *continuation; + + continuation = strchr( cvalue, '/' ); + if ( continuation ) { + /* FIXME: this makes sense only in searches */ + *continuation++ = '\0'; + if ( strcasecmp( continuation, "chainingPreferred" ) == 0 ) { + chainingContinuation = LDAP_CHAINING_PREFERRED; + } else if ( strcasecmp( continuation, "chainingRequired" ) == 0 ) { + chainingContinuation = LDAP_CHAINING_REQUIRED; + } else if ( strcasecmp( continuation, "referralsPreferred" ) == 0 ) { + chainingContinuation = LDAP_REFERRALS_PREFERRED; + } else if ( strcasecmp( continuation, "referralsRequired" ) == 0 ) { + chainingContinuation = LDAP_REFERRALS_REQUIRED; + } else { + fprintf( stderr, + "chaining behavior control " + "continuation value \"%s\" invalid\n", + continuation ); + exit( EXIT_FAILURE ); + } + } + + if ( strcasecmp( cvalue, "chainingPreferred" ) == 0 ) { + chainingResolve = LDAP_CHAINING_PREFERRED; + } else if ( strcasecmp( cvalue, "chainingRequired" ) == 0 ) { + chainingResolve = LDAP_CHAINING_REQUIRED; + } else if ( strcasecmp( cvalue, "referralsPreferred" ) == 0 ) { + chainingResolve = LDAP_REFERRALS_PREFERRED; + } else if ( strcasecmp( cvalue, "referralsRequired" ) == 0 ) { + chainingResolve = LDAP_REFERRALS_REQUIRED; + } else { + fprintf( stderr, + "chaining behavior control " + "resolve value \"%s\" invalid\n", + cvalue); + exit( EXIT_FAILURE ); + } + } +#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ + +#ifdef LDAP_CONTROL_X_SESSION_TRACKING + } else if ( strcasecmp( control, "sessiontracking" ) == 0 ) { + if ( sessionTracking ) { + fprintf( stderr, "%s: session tracking can be only specified once\n", prog ); + exit( EXIT_FAILURE ); + } + sessionTracking = 1; + if( crit ) { + fprintf( stderr, "sessiontracking: critical flag not allowed\n" ); + usage(); + } +#endif /* LDAP_CONTROL_X_SESSION_TRACKING */ + + /* this shouldn't go here, really; but it's a feature... */ + } else if ( strcasecmp( control, "abandon" ) == 0 ) { + abcan = Intr_Abandon; + if ( crit ) { + gotintr = abcan; + } + + } else if ( strcasecmp( control, "cancel" ) == 0 ) { + abcan = Intr_Cancel; + if ( crit ) { + gotintr = abcan; + } + + } else if ( strcasecmp( control, "ignore" ) == 0 ) { + abcan = Intr_Ignore; + if ( crit ) { + gotintr = abcan; + } + + } else if ( strcasecmp( control, "backlog" ) == 0 ) { + /* special search: accumulate lots of responses + * but don't read any, force slapd writer to wait. + * Then abandon the search and issue a new one. + */ + backlog = 1; + + } else if ( tool_is_oid( control ) ) { + LDAPControl *tmpctrls, ctrl; + + tmpctrls = (LDAPControl *)ber_memrealloc( unknown_ctrls, + (unknown_ctrls_num + 1)*sizeof( LDAPControl ) ); + if ( tmpctrls == NULL ) { + fprintf( stderr, "%s: no memory?\n", prog ); + exit( EXIT_FAILURE ); + } + unknown_ctrls = tmpctrls; + ctrl.ldctl_oid = control; + /* don't free it */ + control = NULL; + ctrl.ldctl_value.bv_val = NULL; + ctrl.ldctl_value.bv_len = 0; + ctrl.ldctl_iscritical = crit; + + if ( cvalue != NULL ) { + struct berval bv; + size_t len = strlen( cvalue ); + int retcode; + + bv.bv_len = LUTIL_BASE64_DECODE_LEN( len ); + bv.bv_val = ber_memalloc( bv.bv_len + 1 ); + + retcode = lutil_b64_pton( cvalue, + (unsigned char *)bv.bv_val, + bv.bv_len ); + + if ( retcode == -1 || (unsigned) retcode > bv.bv_len ) { + fprintf( stderr, "Unable to parse value of general control %s\n", + control ); + usage(); + } + + bv.bv_len = retcode; + ctrl.ldctl_value = bv; + } + + unknown_ctrls[ unknown_ctrls_num ] = ctrl; + unknown_ctrls_num++; + + } else { + fprintf( stderr, "Invalid general control name: %s\n", + control ); + usage(); + } + if ( control ) { + ber_memfree( control ); + control = NULL; + } + break; + case 'f': /* read from file */ + if( infile != NULL ) { + fprintf( stderr, "%s: -f previously specified\n", prog ); + exit( EXIT_FAILURE ); + } + infile = ber_strdup( optarg ); + break; + case 'h': /* ldap host */ + if( ldaphost != NULL ) { + fprintf( stderr, "%s: -h previously specified\n", prog ); + exit( EXIT_FAILURE ); + } + ldaphost = ber_strdup( optarg ); + break; + case 'H': /* ldap URI */ + if( ldapuri != NULL ) { + fprintf( stderr, "%s: -H previously specified\n", prog ); + exit( EXIT_FAILURE ); + } + ldapuri = ber_strdup( optarg ); + break; + case 'I': +#ifdef HAVE_CYRUS_SASL + if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) { + fprintf( stderr, "%s: incompatible previous " + "authentication choice\n", + prog ); + exit( EXIT_FAILURE ); + } + authmethod = LDAP_AUTH_SASL; + sasl_flags = LDAP_SASL_INTERACTIVE; + break; +#else + fprintf( stderr, "%s: was not compiled with SASL support\n", + prog ); + exit( EXIT_FAILURE ); +#endif + case 'M': + /* enable Manage DSA IT */ + manageDSAit++; + break; + case 'n': /* print operations, don't actually do them */ + dont++; + break; + case 'N': + nocanon++; + break; + case 'o': + control = ber_strdup( optarg ); + if ( (cvalue = strchr( control, '=' )) != NULL ) { + *cvalue++ = '\0'; + } + + if ( strcasecmp( control, "nettimeout" ) == 0 ) { + if( nettimeout.tv_sec != -1 ) { + fprintf( stderr, "nettimeout option previously specified\n"); + exit( EXIT_FAILURE ); + } + if( cvalue == NULL || cvalue[0] == '\0' ) { + fprintf( stderr, "nettimeout: option value expected\n" ); + usage(); + } + if ( strcasecmp( cvalue, "none" ) == 0 ) { + nettimeout.tv_sec = 0; + } else if ( strcasecmp( cvalue, "max" ) == 0 ) { + nettimeout.tv_sec = LDAP_MAXINT; + } else { + ival = strtol( cvalue, &next, 10 ); + if ( next == NULL || next[0] != '\0' ) { + fprintf( stderr, + _("Unable to parse network timeout \"%s\"\n"), cvalue ); + exit( EXIT_FAILURE ); + } + nettimeout.tv_sec = ival; + } + if( nettimeout.tv_sec < 0 || nettimeout.tv_sec > LDAP_MAXINT ) { + fprintf( stderr, _("%s: invalid network timeout (%ld) specified\n"), + prog, (long)nettimeout.tv_sec ); + exit( EXIT_FAILURE ); + } + + } else if ( strcasecmp( control, "ldif-wrap" ) == 0 ) { + if ( cvalue == 0 ) { + ldif_wrap = LDIF_LINE_WIDTH; + + } else if ( strcasecmp( cvalue, "no" ) == 0 ) { + ldif_wrap = LDIF_LINE_WIDTH_MAX; + + } else { + unsigned int u; + if ( lutil_atou( &u, cvalue ) ) { + fprintf( stderr, + _("Unable to parse ldif-wrap=\"%s\"\n"), cvalue ); + exit( EXIT_FAILURE ); + } + ldif_wrap = (ber_len_t)u; + } + + } else { + fprintf( stderr, "Invalid general option name: %s\n", + control ); + usage(); + } + ber_memfree(control); + break; + case 'O': +#ifdef HAVE_CYRUS_SASL + if( sasl_secprops != NULL ) { + fprintf( stderr, "%s: -O previously specified\n", prog ); + exit( EXIT_FAILURE ); + } + if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) { + fprintf( stderr, "%s: incompatible previous " + "authentication choice\n", prog ); + exit( EXIT_FAILURE ); + } + authmethod = LDAP_AUTH_SASL; + sasl_secprops = ber_strdup( optarg ); +#else + fprintf( stderr, "%s: not compiled with SASL support\n", prog ); + exit( EXIT_FAILURE ); +#endif + break; + case 'p': + if( ldapport ) { + fprintf( stderr, "%s: -p previously specified\n", prog ); + exit( EXIT_FAILURE ); + } + ival = strtol( optarg, &next, 10 ); + if ( next == NULL || next[0] != '\0' ) { + fprintf( stderr, "%s: unable to parse port number \"%s\"\n", prog, optarg ); + exit( EXIT_FAILURE ); + } + ldapport = ival; + break; + case 'P': + ival = strtol( optarg, &next, 10 ); + if ( next == NULL || next[0] != '\0' ) { + fprintf( stderr, "%s: unable to parse protocol version \"%s\"\n", prog, optarg ); + exit( EXIT_FAILURE ); + } + switch( ival ) { + case 2: + if( protocol == LDAP_VERSION3 ) { + fprintf( stderr, "%s: -P 2 incompatible with version %d\n", + prog, protocol ); + exit( EXIT_FAILURE ); + } + protocol = LDAP_VERSION2; + break; + case 3: + if( protocol == LDAP_VERSION2 ) { + fprintf( stderr, "%s: -P 2 incompatible with version %d\n", + prog, protocol ); + exit( EXIT_FAILURE ); + } + protocol = LDAP_VERSION3; + break; + default: + fprintf( stderr, "%s: protocol version should be 2 or 3\n", + prog ); + usage(); + } + break; + case 'Q': +#ifdef HAVE_CYRUS_SASL + if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) { + fprintf( stderr, "%s: incompatible previous " + "authentication choice\n", + prog ); + exit( EXIT_FAILURE ); + } + authmethod = LDAP_AUTH_SASL; + sasl_flags = LDAP_SASL_QUIET; + break; +#else + fprintf( stderr, "%s: not compiled with SASL support\n", + prog ); + exit( EXIT_FAILURE ); +#endif + case 'R': +#ifdef HAVE_CYRUS_SASL + if( sasl_realm != NULL ) { + fprintf( stderr, "%s: -R previously specified\n", prog ); + exit( EXIT_FAILURE ); + } + if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) { + fprintf( stderr, "%s: incompatible previous " + "authentication choice\n", + prog ); + exit( EXIT_FAILURE ); + } + authmethod = LDAP_AUTH_SASL; + sasl_realm = ber_strdup( optarg ); +#else + fprintf( stderr, "%s: not compiled with SASL support\n", + prog ); + exit( EXIT_FAILURE ); +#endif + break; + case 'U': +#ifdef HAVE_CYRUS_SASL + if( sasl_authc_id != NULL ) { + fprintf( stderr, "%s: -U previously specified\n", prog ); + exit( EXIT_FAILURE ); + } + if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) { + fprintf( stderr, "%s: incompatible previous " + "authentication choice\n", + prog ); + exit( EXIT_FAILURE ); + } + authmethod = LDAP_AUTH_SASL; + sasl_authc_id = ber_strdup( optarg ); +#else + fprintf( stderr, "%s: not compiled with SASL support\n", + prog ); + exit( EXIT_FAILURE ); +#endif + break; + case 'v': /* verbose mode */ + verbose++; + break; + case 'V': /* version */ + version++; + break; + case 'w': /* password */ + passwd.bv_val = ber_strdup( optarg ); + { + char* p; + + for( p = optarg; *p != '\0'; p++ ) { + *p = '\0'; + } + } + passwd.bv_len = strlen( passwd.bv_val ); + break; + case 'W': + want_bindpw++; + break; + case 'y': + pw_file = optarg; + break; + case 'Y': +#ifdef HAVE_CYRUS_SASL + if( sasl_mech != NULL ) { + fprintf( stderr, "%s: -Y previously specified\n", prog ); + exit( EXIT_FAILURE ); + } + if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) { + fprintf( stderr, + "%s: incompatible with authentication choice\n", prog ); + exit( EXIT_FAILURE ); + } + authmethod = LDAP_AUTH_SASL; + sasl_mech = ber_strdup( optarg ); +#else + fprintf( stderr, "%s: not compiled with SASL support\n", prog ); + exit( EXIT_FAILURE ); +#endif + break; + case 'x': + if( authmethod != -1 && authmethod != LDAP_AUTH_SIMPLE ) { + fprintf( stderr, "%s: incompatible with previous " + "authentication choice\n", prog ); + exit( EXIT_FAILURE ); + } + authmethod = LDAP_AUTH_SIMPLE; + break; + case 'X': +#ifdef HAVE_CYRUS_SASL + if( sasl_authz_id != NULL ) { + fprintf( stderr, "%s: -X previously specified\n", prog ); + exit( EXIT_FAILURE ); + } + if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) { + fprintf( stderr, "%s: -X incompatible with " + "authentication choice\n", prog ); + exit( EXIT_FAILURE ); + } + authmethod = LDAP_AUTH_SASL; + sasl_authz_id = ber_strdup( optarg ); +#else + fprintf( stderr, "%s: not compiled with SASL support\n", prog ); + exit( EXIT_FAILURE ); +#endif + break; + case 'Z': +#ifdef HAVE_TLS + use_tls++; +#else + fprintf( stderr, "%s: not compiled with TLS support\n", prog ); + exit( EXIT_FAILURE ); +#endif + break; + default: + if( handle_private_option( i ) ) break; + fprintf( stderr, "%s: unrecognized option -%c\n", + prog, optopt ); + usage(); + } + } + + { + /* prevent bad linking */ + LDAPAPIInfo api; + api.ldapai_info_version = LDAP_API_INFO_VERSION; + + if ( ldap_get_option(NULL, LDAP_OPT_API_INFO, &api) + != LDAP_OPT_SUCCESS ) + { + fprintf( stderr, "%s: ldap_get_option(API_INFO) failed\n", prog ); + exit( EXIT_FAILURE ); + } + + if (api.ldapai_info_version != LDAP_API_INFO_VERSION) { + fprintf( stderr, "LDAP APIInfo version mismatch: " + "library %d, header %d\n", + api.ldapai_info_version, LDAP_API_INFO_VERSION ); + exit( EXIT_FAILURE ); + } + + if( api.ldapai_api_version != LDAP_API_VERSION ) { + fprintf( stderr, "LDAP API version mismatch: " + "library %d, header %d\n", + api.ldapai_api_version, LDAP_API_VERSION ); + exit( EXIT_FAILURE ); + } + + if( strcmp(api.ldapai_vendor_name, LDAP_VENDOR_NAME ) != 0 ) { + fprintf( stderr, "LDAP vendor name mismatch: " + "library %s, header %s\n", + api.ldapai_vendor_name, LDAP_VENDOR_NAME ); + exit( EXIT_FAILURE ); + } + + if( api.ldapai_vendor_version != LDAP_VENDOR_VERSION ) { + fprintf( stderr, "LDAP vendor version mismatch: " + "library %d, header %d\n", + api.ldapai_vendor_version, LDAP_VENDOR_VERSION ); + exit( EXIT_FAILURE ); + } + + if (version) { + fprintf( stderr, "%s: %s\t(LDAP library: %s %d)\n", + prog, __Version, + LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION ); + if (version > 1) exit( EXIT_SUCCESS ); + } + + ldap_memfree( api.ldapai_vendor_name ); + ber_memvfree( (void **)api.ldapai_extensions ); + } + + if (protocol == -1) + protocol = LDAP_VERSION3; + + if (authmethod == -1 && protocol > LDAP_VERSION2) { +#ifdef HAVE_CYRUS_SASL + if ( binddn != NULL ) { + authmethod = LDAP_AUTH_SIMPLE; + } else { + authmethod = LDAP_AUTH_SASL; + } +#else + authmethod = LDAP_AUTH_SIMPLE; +#endif + } + + if( ldapuri == NULL ) { + if( ldapport && ( ldaphost == NULL )) { + fprintf( stderr, "%s: -p without -h is invalid.\n", prog ); + exit( EXIT_FAILURE ); + } + } else { + if( ldaphost != NULL ) { + fprintf( stderr, "%s: -H incompatible with -h\n", prog ); + exit( EXIT_FAILURE ); + } + if( ldapport ) { + fprintf( stderr, "%s: -H incompatible with -p\n", prog ); + exit( EXIT_FAILURE ); + } + } + + if( protocol == LDAP_VERSION2 ) { + if( assertctl || authzid || manageDIT || manageDSAit || +#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ + proxydn || +#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */ +#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR + chaining || +#endif +#ifdef LDAP_CONTROL_X_SESSION_TRACKING + sessionTracking || +#endif /* LDAP_CONTROL_X_SESSION_TRACKING */ + noop || ppolicy || preread || postread ) + { + fprintf( stderr, "%s: -e/-M incompatible with LDAPv2\n", prog ); + exit( EXIT_FAILURE ); + } +#ifdef HAVE_TLS + if( use_tls ) { + fprintf( stderr, "%s: -Z incompatible with LDAPv2\n", prog ); + exit( EXIT_FAILURE ); + } +#endif +#ifdef HAVE_CYRUS_SASL + if( authmethod == LDAP_AUTH_SASL ) { + fprintf( stderr, "%s: -[IOQRUXY] incompatible with LDAPv2\n", + prog ); + exit( EXIT_FAILURE ); + } +#endif + } + + if ( ( pw_file || want_bindpw ) && !BER_BVISNULL( &passwd ) ) { + fprintf( stderr, "%s: -%c incompatible with -w\n", + prog, ( pw_file ? 'y' : 'W' ) ); + exit( EXIT_FAILURE ); + } +} + + +LDAP * +tool_conn_setup( int dont, void (*private_setup)( LDAP * ) ) +{ + LDAP *ld = NULL; + + if ( debug ) { + if( ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &debug ) + != LBER_OPT_SUCCESS ) + { + fprintf( stderr, + "Could not set LBER_OPT_DEBUG_LEVEL %d\n", debug ); + } + if( ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &debug ) + != LDAP_OPT_SUCCESS ) + { + fprintf( stderr, + "Could not set LDAP_OPT_DEBUG_LEVEL %d\n", debug ); + } + } + +#ifdef SIGPIPE + (void) SIGNAL( SIGPIPE, SIG_IGN ); +#endif + + if ( abcan ) { + SIGNAL( SIGINT, do_sig ); + } + + if ( !dont ) { + int rc; + + if( ( ldaphost != NULL || ldapport ) && ( ldapuri == NULL ) ) { + /* construct URL */ + LDAPURLDesc url; + memset( &url, 0, sizeof(url)); + + url.lud_scheme = "ldap"; + url.lud_host = ldaphost; + url.lud_port = ldapport; + url.lud_scope = LDAP_SCOPE_DEFAULT; + + ldapuri = ldap_url_desc2str( &url ); + + } else if ( ldapuri != NULL ) { + LDAPURLDesc *ludlist, **ludp; + char **urls = NULL; + int nurls = 0; + + rc = ldap_url_parselist( &ludlist, ldapuri ); + if ( rc != LDAP_URL_SUCCESS ) { + fprintf( stderr, + "Could not parse LDAP URI(s)=%s (%d)\n", + ldapuri, rc ); + exit( EXIT_FAILURE ); + } + + for ( ludp = &ludlist; *ludp != NULL; ) { + LDAPURLDesc *lud = *ludp; + char **tmp; + + if ( lud->lud_dn != NULL && lud->lud_dn[ 0 ] != '\0' && + ( lud->lud_host == NULL || lud->lud_host[0] == '\0' ) ) + { + /* if no host but a DN is provided, + * use DNS SRV to gather the host list + * and turn it into a list of URIs + * using the scheme provided */ + char *domain = NULL, + *hostlist = NULL, + **hosts = NULL; + int i, + len_proto = strlen( lud->lud_scheme ); + + if ( ldap_dn2domain( lud->lud_dn, &domain ) + || domain == NULL ) + { + fprintf( stderr, + "DNS SRV: Could not turn " + "DN=\"%s\" into a domain\n", + lud->lud_dn ); + goto dnssrv_free; + } + + rc = ldap_domain2hostlist( domain, &hostlist ); + if ( rc ) { + fprintf( stderr, + "DNS SRV: Could not turn " + "domain=%s into a hostlist\n", + domain ); + goto dnssrv_free; + } + + hosts = ldap_str2charray( hostlist, " " ); + if ( hosts == NULL ) { + fprintf( stderr, + "DNS SRV: Could not parse " + "hostlist=\"%s\"\n", + hostlist ); + goto dnssrv_free; + } + + for ( i = 0; hosts[ i ] != NULL; i++ ) + /* count'em */ ; + + tmp = (char **)ber_memrealloc( urls, sizeof( char * ) * ( nurls + i + 1 ) ); + if ( tmp == NULL ) { + fprintf( stderr, + "DNS SRV: out of memory?\n" ); + goto dnssrv_free; + } + urls = tmp; + urls[ nurls ] = NULL; + + for ( i = 0; hosts[ i ] != NULL; i++ ) { + size_t len = len_proto + + STRLENOF( "://" ) + + strlen( hosts[ i ] ) + + 1; + + urls[ nurls + i + 1 ] = NULL; + urls[ nurls + i ] = (char *)malloc( sizeof( char ) * len ); + if ( urls[ nurls + i ] == NULL ) { + fprintf( stderr, + "DNS SRV: out of memory?\n" ); + goto dnssrv_free; + } + + snprintf( urls[ nurls + i ], len, "%s://%s", + lud->lud_scheme, hosts[ i ] ); + } + nurls += i; + +dnssrv_free:; + ber_memvfree( (void **)hosts ); + ber_memfree( hostlist ); + ber_memfree( domain ); + + } else { + tmp = (char **)ber_memrealloc( urls, sizeof( char * ) * ( nurls + 2 ) ); + if ( tmp == NULL ) { + fprintf( stderr, + "DNS SRV: out of memory?\n" ); + break; + } + urls = tmp; + urls[ nurls + 1 ] = NULL; + + urls[ nurls ] = ldap_url_desc2str( lud ); + if ( urls[ nurls ] == NULL ) { + fprintf( stderr, + "DNS SRV: out of memory?\n" ); + break; + } + nurls++; + } + + *ludp = lud->lud_next; + + lud->lud_next = NULL; + ldap_free_urldesc( lud ); + } + + if ( ludlist != NULL ) { + ldap_free_urllist( ludlist ); + exit( EXIT_FAILURE ); + + } else if ( urls == NULL ) { + exit( EXIT_FAILURE ); + } + + ldap_memfree( ldapuri ); + ldapuri = ldap_charray2str( urls, " " ); + ber_memvfree( (void **)urls ); + } + + if ( verbose ) { + fprintf( stderr, "ldap_initialize( %s )\n", + ldapuri != NULL ? ldapuri : "<DEFAULT>" ); + } + rc = ldap_initialize( &ld, ldapuri ); + if( rc != LDAP_SUCCESS ) { + fprintf( stderr, + "Could not create LDAP session handle for URI=%s (%d): %s\n", + ldapuri, rc, ldap_err2string(rc) ); + exit( EXIT_FAILURE ); + } + + if( private_setup ) private_setup( ld ); + + /* referrals: obsolete */ + if( ldap_set_option( ld, LDAP_OPT_REFERRALS, + referrals ? LDAP_OPT_ON : LDAP_OPT_OFF ) != LDAP_OPT_SUCCESS ) + { + fprintf( stderr, "Could not set LDAP_OPT_REFERRALS %s\n", + referrals ? "on" : "off" ); + tool_exit( ld, EXIT_FAILURE ); + } + +#ifdef HAVE_CYRUS_SASL + /* canon */ + if( nocanon ) { + if( ldap_set_option( ld, LDAP_OPT_X_SASL_NOCANON, + LDAP_OPT_ON ) != LDAP_OPT_SUCCESS ) + { + fprintf( stderr, "Could not set LDAP_OPT_X_SASL_NOCANON on\n" ); + tool_exit( ld, EXIT_FAILURE ); + } + } +#endif + if( ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &protocol ) + != LDAP_OPT_SUCCESS ) + { + fprintf( stderr, "Could not set LDAP_OPT_PROTOCOL_VERSION %d\n", + protocol ); + tool_exit( ld, EXIT_FAILURE ); + } + + if ( use_tls ) { + rc = ldap_start_tls_s( ld, NULL, NULL ); + if ( rc != LDAP_SUCCESS ) { + char *msg=NULL; + ldap_get_option( ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*)&msg); + tool_perror( "ldap_start_tls", rc, NULL, NULL, msg, NULL ); + ldap_memfree(msg); + if ( use_tls > 1 ) { + tool_exit( ld, EXIT_FAILURE ); + } + } + } + + if ( nettimeout.tv_sec > 0 ) { + if ( ldap_set_option( ld, LDAP_OPT_NETWORK_TIMEOUT, (void *) &nettimeout ) + != LDAP_OPT_SUCCESS ) + { + fprintf( stderr, "Could not set LDAP_OPT_NETWORK_TIMEOUT %ld\n", + (long)nettimeout.tv_sec ); + tool_exit( ld, EXIT_FAILURE ); + } + } + } + + return ld; +} + + +void +tool_bind( LDAP *ld ) +{ + LDAPControl **sctrlsp = NULL; + LDAPControl *sctrls[3]; + LDAPControl sctrl[3]; + int nsctrls = 0; + +#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST + if ( ppolicy ) { + LDAPControl c; + c.ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST; + c.ldctl_value.bv_val = NULL; + c.ldctl_value.bv_len = 0; + c.ldctl_iscritical = 0; + sctrl[nsctrls] = c; + sctrls[nsctrls] = &sctrl[nsctrls]; + sctrls[++nsctrls] = NULL; + } +#endif + +#ifdef LDAP_CONTROL_X_SESSION_TRACKING + if ( sessionTracking ) { + LDAPControl c; + + if ( BER_BVISNULL( &stValue) && st_value( ld, &stValue ) ) { + tool_exit( ld, EXIT_FAILURE ); + } + + c.ldctl_oid = LDAP_CONTROL_X_SESSION_TRACKING; + c.ldctl_iscritical = 0; + ber_dupbv( &c.ldctl_value, &stValue ); + + sctrl[nsctrls] = c; + sctrls[nsctrls] = &sctrl[nsctrls]; + sctrls[++nsctrls] = NULL; + } +#endif /* LDAP_CONTROL_X_SESSION_TRACKING */ + + if ( nsctrls ) { + sctrlsp = sctrls; + } + + assert( nsctrls < (int) (sizeof(sctrls)/sizeof(sctrls[0])) ); + + if ( pw_file || want_bindpw ) { + assert( passwd.bv_val == NULL && passwd.bv_len == 0 ); + + if ( pw_file ) { + if ( lutil_get_filed_password( pw_file, &passwd ) ) { + tool_exit( ld, EXIT_FAILURE ); + } + + } else { + char *pw = getpassphrase( _("Enter LDAP Password: ") ); + if ( pw ) { + passwd.bv_val = ber_strdup( pw ); + passwd.bv_len = strlen( passwd.bv_val ); + } + } + } + + if ( authmethod == LDAP_AUTH_SASL ) { +#ifdef HAVE_CYRUS_SASL + void *defaults; + int rc; + + if( sasl_secprops != NULL ) { + rc = ldap_set_option( ld, LDAP_OPT_X_SASL_SECPROPS, + (void *) sasl_secprops ); + + if( rc != LDAP_OPT_SUCCESS ) { + fprintf( stderr, + "Could not set LDAP_OPT_X_SASL_SECPROPS: %s\n", + sasl_secprops ); + tool_exit( ld, LDAP_LOCAL_ERROR ); + } + } + + defaults = lutil_sasl_defaults( ld, + sasl_mech, + sasl_realm, + sasl_authc_id, + passwd.bv_val, + sasl_authz_id ); + + rc = ldap_sasl_interactive_bind_s( ld, binddn, sasl_mech, + sctrlsp, + NULL, sasl_flags, lutil_sasl_interact, defaults ); + + lutil_sasl_freedefs( defaults ); + if( rc != LDAP_SUCCESS ) { + char *msg=NULL; + ldap_get_option( ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*)&msg); + tool_perror( "ldap_sasl_interactive_bind_s", + rc, NULL, NULL, msg, NULL ); + ldap_memfree(msg); + tool_exit( ld, rc ); + } +#else + fprintf( stderr, "%s: not compiled with SASL support\n", prog ); + tool_exit( ld, LDAP_NOT_SUPPORTED ); +#endif + } else { + int msgid, err, rc; + LDAPMessage *result; + LDAPControl **ctrls; + char msgbuf[256]; + char *matched = NULL; + char *info = NULL; + char **refs = NULL; + + msgbuf[0] = 0; + + { + /* simple bind */ + rc = ldap_sasl_bind( ld, binddn, LDAP_SASL_SIMPLE, &passwd, + sctrlsp, NULL, &msgid ); + if ( msgid == -1 ) { + tool_perror( "ldap_sasl_bind(SIMPLE)", rc, + NULL, NULL, NULL, NULL ); + tool_exit( ld, rc ); + } + } + + rc = ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result ); + if ( rc == -1 ) { + tool_perror( "ldap_result", -1, NULL, NULL, NULL, NULL ); + tool_exit( ld, LDAP_LOCAL_ERROR ); + } + + if ( rc == 0 ) { + tool_perror( "ldap_result", LDAP_TIMEOUT, NULL, NULL, NULL, NULL ); + tool_exit( ld, LDAP_LOCAL_ERROR ); + } + + if ( result ) { + rc = ldap_parse_result( ld, result, &err, &matched, &info, &refs, + &ctrls, 1 ); + if ( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_bind parse result", rc, NULL, matched, info, refs ); + tool_exit( ld, LDAP_LOCAL_ERROR ); + } + } + +#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST + if ( ctrls && ppolicy ) { + LDAPControl *ctrl; + int expire, grace, len = 0; + LDAPPasswordPolicyError pErr = -1; + + ctrl = ldap_control_find( LDAP_CONTROL_PASSWORDPOLICYRESPONSE, + ctrls, NULL ); + + if ( ctrl && ldap_parse_passwordpolicy_control( ld, ctrl, + &expire, &grace, &pErr ) == LDAP_SUCCESS ) + { + if ( pErr != PP_noError ){ + msgbuf[0] = ';'; + msgbuf[1] = ' '; + strcpy( msgbuf+2, ldap_passwordpolicy_err2txt( pErr )); + len = strlen( msgbuf ); + } + if ( expire >= 0 ) { + sprintf( msgbuf+len, + " (Password expires in %d seconds)", + expire ); + } else if ( grace >= 0 ) { + sprintf( msgbuf+len, + " (Password expired, %d grace logins remain)", + grace ); + } + } + } +#endif + +#ifdef LDAP_CONTROL_X_PASSWORD_EXPIRED + if ( ctrls ) { + LDAPControl *ctrl; + ctrl = ldap_control_find( LDAP_CONTROL_X_PASSWORD_EXPIRED, + ctrls, NULL ); + if ( !ctrl ) + ctrl = ldap_control_find( LDAP_CONTROL_X_PASSWORD_EXPIRING, + ctrls, NULL ); + if ( ctrl ) { + LDAPControl *ctmp[2]; + ctmp[0] = ctrl; + ctmp[1] = NULL; + tool_print_ctrls( ld, ctmp ); + } + } +#endif + + if ( ctrls ) { + ldap_controls_free( ctrls ); + } + + if ( err != LDAP_SUCCESS + || msgbuf[0] + || ( matched && matched[ 0 ] ) + || ( info && info[ 0 ] ) + || refs ) + { + tool_perror( "ldap_bind", err, msgbuf, matched, info, refs ); + + if( matched ) ber_memfree( matched ); + if( info ) ber_memfree( info ); + if( refs ) ber_memvfree( (void **)refs ); + + if ( err != LDAP_SUCCESS ) tool_exit( ld, err ); + } + } +} + +void +tool_unbind( LDAP *ld ) +{ + int err = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, NULL ); + + if ( err != LDAP_OPT_SUCCESS ) { + fprintf( stderr, "Could not unset controls\n"); + } + + (void) ldap_unbind_ext( ld, NULL, NULL ); +} + +void +tool_exit( LDAP *ld, int status ) +{ + if ( ld != NULL ) { + tool_unbind( ld ); + } + tool_destroy(); + exit( status ); +} + + +/* Set server controls. Add controls extra_c[0..count-1], if set. */ +void +tool_server_controls( LDAP *ld, LDAPControl *extra_c, int count ) +{ + int i = 0, j, crit = 0, err; + LDAPControl c[16], **ctrls; + + if ( ! ( assertctl + || authzid +#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ + || proxydn +#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */ + || manageDIT + || manageDSAit + || noop +#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST + || ppolicy +#endif + || preread + || postread +#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR + || chaining +#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ +#ifdef LDAP_CONTROL_X_SESSION_TRACKING + || sessionTracking +#endif /* LDAP_CONTROL_X_SESSION_TRACKING */ + || count + || unknown_ctrls_num ) ) + { + return; + } + + ctrls = (LDAPControl**) malloc(sizeof(c) + (count + unknown_ctrls_num + 1)*sizeof(LDAPControl*)); + if ( ctrls == NULL ) { + fprintf( stderr, "No memory\n" ); + tool_exit( ld, EXIT_FAILURE ); + } + + if ( assertctl ) { + if ( BER_BVISNULL( &assertionvalue ) ) { + err = ldap_create_assertion_control_value( ld, + assertion, &assertionvalue ); + if ( err ) { + fprintf( stderr, + "Unable to create assertion value " + "\"%s\" (%d)\n", assertion, err ); + } + } + + c[i].ldctl_oid = LDAP_CONTROL_ASSERT; + c[i].ldctl_value = assertionvalue; + c[i].ldctl_iscritical = assertctl > 1; + ctrls[i] = &c[i]; + i++; + } + + if ( authzid ) { + c[i].ldctl_value.bv_val = authzid; + c[i].ldctl_value.bv_len = strlen( authzid ); + c[i].ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ; + c[i].ldctl_iscritical = 1; + ctrls[i] = &c[i]; + i++; + } + +#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ + /* NOTE: doesn't need an extra count because it's incompatible + * with authzid */ + if ( proxydn ) { + BerElementBuffer berbuf; + BerElement *ber = (BerElement *)&berbuf; + + ber_init2( ber, NULL, LBER_USE_DER ); + + if ( ber_printf( ber, "s", proxydn ) == -1 ) { + tool_exit( ld, EXIT_FAILURE ); + } + + if ( ber_flatten2( ber, &c[i].ldctl_value, 0 ) == -1 ) { + tool_exit( ld, EXIT_FAILURE ); + } + + c[i].ldctl_oid = LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ; + c[i].ldctl_iscritical = 1; + ctrls[i] = &c[i]; + i++; + } +#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */ + + if ( manageDIT ) { + c[i].ldctl_oid = LDAP_CONTROL_MANAGEDIT; + BER_BVZERO( &c[i].ldctl_value ); + c[i].ldctl_iscritical = manageDIT > 1; + ctrls[i] = &c[i]; + i++; + } + + if ( manageDSAit ) { + c[i].ldctl_oid = LDAP_CONTROL_MANAGEDSAIT; + BER_BVZERO( &c[i].ldctl_value ); + c[i].ldctl_iscritical = manageDSAit > 1; + ctrls[i] = &c[i]; + i++; + } + + if ( noop ) { + c[i].ldctl_oid = LDAP_CONTROL_NOOP; + BER_BVZERO( &c[i].ldctl_value ); + c[i].ldctl_iscritical = noop > 1; + ctrls[i] = &c[i]; + i++; + } + +#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST + if ( ppolicy ) { + c[i].ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST; + BER_BVZERO( &c[i].ldctl_value ); + c[i].ldctl_iscritical = 0; + ctrls[i] = &c[i]; + i++; + } +#endif + + if ( preread ) { + BerElementBuffer berbuf; + BerElement *ber = (BerElement *)&berbuf; + char **attrs = NULL; + + if( preread_attrs ) { + attrs = ldap_str2charray( preread_attrs, "," ); + } + + ber_init2( ber, NULL, LBER_USE_DER ); + + if( ber_printf( ber, "{v}", attrs ) == -1 ) { + fprintf( stderr, "preread attrs encode failed.\n" ); + tool_exit( ld, EXIT_FAILURE ); + } + + err = ber_flatten2( ber, &c[i].ldctl_value, 0 ); + if( err < 0 ) { + fprintf( stderr, "preread flatten failed (%d)\n", err ); + tool_exit( ld, EXIT_FAILURE ); + } + + c[i].ldctl_oid = LDAP_CONTROL_PRE_READ; + c[i].ldctl_iscritical = preread > 1; + ctrls[i] = &c[i]; + i++; + + if( attrs ) ldap_charray_free( attrs ); + } + + if ( postread ) { + BerElementBuffer berbuf; + BerElement *ber = (BerElement *)&berbuf; + char **attrs = NULL; + + if( postread_attrs ) { + attrs = ldap_str2charray( postread_attrs, "," ); + } + + ber_init2( ber, NULL, LBER_USE_DER ); + + if( ber_printf( ber, "{v}", attrs ) == -1 ) { + fprintf( stderr, "postread attrs encode failed.\n" ); + tool_exit( ld, EXIT_FAILURE ); + } + + err = ber_flatten2( ber, &c[i].ldctl_value, 0 ); + if( err < 0 ) { + fprintf( stderr, "postread flatten failed (%d)\n", err ); + tool_exit( ld, EXIT_FAILURE ); + } + + c[i].ldctl_oid = LDAP_CONTROL_POST_READ; + c[i].ldctl_iscritical = postread > 1; + ctrls[i] = &c[i]; + i++; + + if( attrs ) ldap_charray_free( attrs ); + } + +#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR + if ( chaining ) { + if ( chainingResolve > -1 ) { + BerElementBuffer berbuf; + BerElement *ber = (BerElement *)&berbuf; + + ber_init2( ber, NULL, LBER_USE_DER ); + + err = ber_printf( ber, "{e" /* } */, chainingResolve ); + if ( err == -1 ) { + ber_free( ber, 1 ); + fprintf( stderr, _("Chaining behavior control encoding error!\n") ); + tool_exit( ld, EXIT_FAILURE ); + } + + if ( chainingContinuation > -1 ) { + err = ber_printf( ber, "e", chainingContinuation ); + if ( err == -1 ) { + ber_free( ber, 1 ); + fprintf( stderr, _("Chaining behavior control encoding error!\n") ); + tool_exit( ld, EXIT_FAILURE ); + } + } + + err = ber_printf( ber, /* { */ "N}" ); + if ( err == -1 ) { + ber_free( ber, 1 ); + fprintf( stderr, _("Chaining behavior control encoding error!\n") ); + tool_exit( ld, EXIT_FAILURE ); + } + + if ( ber_flatten2( ber, &c[i].ldctl_value, 0 ) == -1 ) { + tool_exit( ld, EXIT_FAILURE ); + } + + } else { + BER_BVZERO( &c[i].ldctl_value ); + } + + c[i].ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR; + c[i].ldctl_iscritical = chaining > 1; + ctrls[i] = &c[i]; + i++; + } +#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ + +#ifdef LDAP_CONTROL_X_SESSION_TRACKING + if ( sessionTracking ) { + if ( BER_BVISNULL( &stValue ) && st_value( ld, &stValue ) ) { + tool_exit( ld, EXIT_FAILURE ); + } + + c[i].ldctl_oid = LDAP_CONTROL_X_SESSION_TRACKING; + c[i].ldctl_iscritical = 0; + ber_dupbv( &c[i].ldctl_value, &stValue ); + + ctrls[i] = &c[i]; + i++; + } +#endif /* LDAP_CONTROL_X_SESSION_TRACKING */ + + while ( count-- ) { + ctrls[i++] = extra_c++; + } + for ( count = 0; count < unknown_ctrls_num; count++ ) { + ctrls[i++] = &unknown_ctrls[count]; + } + ctrls[i] = NULL; + + err = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, ctrls ); + + if ( err != LDAP_OPT_SUCCESS ) { + for ( j = 0; j < i; j++ ) { + if ( ctrls[j]->ldctl_iscritical ) crit = 1; + } + fprintf( stderr, "Could not set %scontrols\n", + crit ? "critical " : "" ); + } + + free( ctrls ); + if ( crit ) { + tool_exit( ld, EXIT_FAILURE ); + } +} + +int +tool_check_abandon( LDAP *ld, int msgid ) +{ + int rc; + LDAPControl *sctrls[1] = { NULL }; + + switch ( gotintr ) { + case Intr_Cancel: + rc = ldap_cancel_s( ld, msgid, sctrls, NULL ); + fprintf( stderr, "got interrupt, cancel got %d: %s\n", + rc, ldap_err2string( rc ) ); + return -1; + + case Intr_Abandon: + rc = ldap_abandon_ext( ld, msgid, sctrls, NULL ); + fprintf( stderr, "got interrupt, abandon got %d: %s\n", + rc, ldap_err2string( rc ) ); + return -1; + + case Intr_Ignore: + /* just unbind, ignoring the request */ + return -1; + } + + return 0; +} + +static int +print_prepostread( LDAP *ld, LDAPControl *ctrl, struct berval *what) +{ + BerElement *ber; + struct berval bv; + + tool_write_ldif( LDIF_PUT_COMMENT, "==> ", + what->bv_val, what->bv_len ); + ber = ber_init( &ctrl->ldctl_value ); + if ( ber == NULL ) { + /* error? */ + return 1; + + } else if ( ber_scanf( ber, "{m{" /*}}*/, &bv ) == LBER_ERROR ) { + /* error? */ + return 1; + + } else { + tool_write_ldif( LDIF_PUT_VALUE, "dn", bv.bv_val, bv.bv_len ); + + while ( ber_scanf( ber, "{m" /*}*/, &bv ) != LBER_ERROR ) { + int i; + BerVarray vals = NULL; + char *str = NULL; + + if ( ber_scanf( ber, "[W]", &vals ) == LBER_ERROR || + vals == NULL ) + { + /* error? */ + return 1; + } + + if ( ldif ) { + char *ptr; + + str = malloc( bv.bv_len + STRLENOF(": ") + 1 ); + + ptr = str; + ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len ); + ptr = lutil_strcopy( ptr, ": " ); + } + + for ( i = 0; vals[ i ].bv_val != NULL; i++ ) { + tool_write_ldif( + ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE, + ldif ? str : bv.bv_val, vals[ i ].bv_val, vals[ i ].bv_len ); + } + + ber_bvarray_free( vals ); + if ( str ) free( str ); + } + } + + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + tool_write_ldif( LDIF_PUT_COMMENT, "<== ", + what->bv_val, what->bv_len ); + + return 0; +} + +static int +print_preread( LDAP *ld, LDAPControl *ctrl ) +{ + static struct berval what = BER_BVC( "preread" ); + + return print_prepostread( ld, ctrl, &what ); +} + +static int +print_postread( LDAP *ld, LDAPControl *ctrl ) +{ + static struct berval what = BER_BVC( "postread" ); + + return print_prepostread( ld, ctrl, &what ); +} + +static int +print_paged_results( LDAP *ld, LDAPControl *ctrl ) +{ + ber_int_t estimate; + + /* note: pr_cookie is being malloced; it's freed + * the next time the control is sent, but the last + * time it's not; we don't care too much, because + * the last time an empty value is returned... */ + if ( ldap_parse_pageresponse_control( ld, ctrl, &estimate, &pr_cookie ) + != LDAP_SUCCESS ) + { + /* error? */ + return 1; + + } else { + /* FIXME: check buffer overflow */ + char buf[ BUFSIZ ], *ptr = buf; + + if ( estimate > 0 ) { + ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ), + "estimate=%d", estimate ); + } + + if ( pr_cookie.bv_len > 0 ) { + struct berval bv; + + bv.bv_len = LUTIL_BASE64_ENCODE_LEN( + pr_cookie.bv_len ) + 1; + bv.bv_val = ber_memalloc( bv.bv_len + 1 ); + + bv.bv_len = lutil_b64_ntop( + (unsigned char *) pr_cookie.bv_val, + pr_cookie.bv_len, + bv.bv_val, bv.bv_len ); + + ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ), + "%scookie=%s", ptr == buf ? "" : " ", + bv.bv_val ); + + ber_memfree( bv.bv_val ); + + pr_morePagedResults = 1; + + } else { + ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ), + "%scookie=", ptr == buf ? "" : " " ); + } + + tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE, + ldif ? "pagedresults: " : "pagedresults", + buf, ptr - buf ); + } + + return 0; +} + +static int +print_sss( LDAP *ld, LDAPControl *ctrl ) +{ + int rc; + ber_int_t err; + char *attr; + + rc = ldap_parse_sortresponse_control( ld, ctrl, &err, &attr ); + if ( rc == LDAP_SUCCESS ) { + char buf[ BUFSIZ ]; + rc = snprintf( buf, sizeof(buf), "(%d) %s%s%s", + err, ldap_err2string(err), attr ? " " : "", attr ? attr : "" ); + + tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE, + ldif ? "sortResult: " : "sortResult", buf, rc ); + } + + return rc; +} + +static int +print_vlv( LDAP *ld, LDAPControl *ctrl ) +{ + int rc; + ber_int_t err; + struct berval bv; + + rc = ldap_parse_vlvresponse_control( ld, ctrl, &vlvPos, &vlvCount, + &vlvContext, &err ); + if ( rc == LDAP_SUCCESS ) { + char buf[ BUFSIZ ]; + + if ( vlvContext && vlvContext->bv_len > 0 ) { + bv.bv_len = LUTIL_BASE64_ENCODE_LEN( + vlvContext->bv_len ) + 1; + bv.bv_val = ber_memalloc( bv.bv_len + 1 ); + + bv.bv_len = lutil_b64_ntop( + (unsigned char *) vlvContext->bv_val, + vlvContext->bv_len, + bv.bv_val, bv.bv_len ); + } else { + bv.bv_val = ""; + bv.bv_len = 0; + } + + rc = snprintf( buf, sizeof(buf), "pos=%d count=%d context=%s (%d) %s", + vlvPos, vlvCount, bv.bv_val, + err, ldap_err2string(err)); + + if ( bv.bv_len ) + ber_memfree( bv.bv_val ); + + tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE, + ldif ? "vlvResult" : "vlvResult", buf, rc ); + } + + return rc; +} + +#ifdef LDAP_CONTROL_X_DEREF +static int +print_deref( LDAP *ld, LDAPControl *ctrl ) +{ + LDAPDerefRes *drhead = NULL, *dr; + int rc; + + rc = ldap_parse_derefresponse_control( ld, ctrl, &drhead ); + if ( rc != LDAP_SUCCESS ) { + return rc; + } + + for ( dr = drhead; dr != NULL; dr = dr->next ) { + LDAPDerefVal *dv; + ber_len_t len; + char *buf, *ptr; + + len = strlen( dr->derefAttr ) + STRLENOF(": "); + + for ( dv = dr->attrVals; dv != NULL; dv = dv->next ) { + if ( dv->vals != NULL ) { + int j; + ber_len_t tlen = strlen(dv->type); + + for ( j = 0; dv->vals[ j ].bv_val != NULL; j++ ) { + len += STRLENOF("<:=>;") + tlen + 4*((dv->vals[ j ].bv_len - 1)/3 + 1); + } + } + } + len += dr->derefVal.bv_len + STRLENOF("\n"); + buf = ldap_memalloc( len + 1 ); + if ( buf == NULL ) { + rc = LDAP_NO_MEMORY; + goto done; + } + + ptr = buf; + ptr = lutil_strcopy( ptr, dr->derefAttr ); + *ptr++ = ':'; + *ptr++ = ' '; + for ( dv = dr->attrVals; dv != NULL; dv = dv->next ) { + if ( dv->vals != NULL ) { + int j; + for ( j = 0; dv->vals[ j ].bv_val != NULL; j++ ) { + int k = ldif_is_not_printable( dv->vals[ j ].bv_val, dv->vals[ j ].bv_len ); + + *ptr++ = '<'; + ptr = lutil_strcopy( ptr, dv->type ); + if ( k ) { + *ptr++ = ':'; + } + *ptr++ = '='; + if ( k ) { + k = lutil_b64_ntop( + (unsigned char *) dv->vals[ j ].bv_val, + dv->vals[ j ].bv_len, + ptr, buf + len - ptr ); + assert( k >= 0 ); + ptr += k; + + } else { + ptr = lutil_memcopy( ptr, dv->vals[ j ].bv_val, dv->vals[ j ].bv_len ); + } + *ptr++ = '>'; + *ptr++ = ';'; + } + } + } + ptr = lutil_strncopy( ptr, dr->derefVal.bv_val, dr->derefVal.bv_len ); + *ptr++ = '\n'; + *ptr = '\0'; + assert( ptr <= buf + len ); + + tool_write_ldif( LDIF_PUT_COMMENT, NULL, buf, ptr - buf); + + ldap_memfree( buf ); + } + + rc = LDAP_SUCCESS; + +done:; + ldap_derefresponse_free( drhead ); + + return rc; +} +#endif + +#ifdef LDAP_CONTROL_X_WHATFAILED +static int +print_whatfailed( LDAP *ld, LDAPControl *ctrl ) +{ + BerElement *ber; + ber_tag_t tag; + ber_len_t siz; + BerVarray bva = NULL; + + /* Create a BerElement from the berval returned in the control. */ + ber = ber_init( &ctrl->ldctl_value ); + + if ( ber == NULL ) { + return LDAP_NO_MEMORY; + } + + siz = sizeof(struct berval); + tag = ber_scanf( ber, "[M]", &bva, &siz, 0 ); + if ( tag != LBER_ERROR ) { + int i; + + tool_write_ldif( LDIF_PUT_COMMENT, " what failed:", NULL, 0 ); + + for ( i = 0; bva[i].bv_val != NULL; i++ ) { + tool_write_ldif( LDIF_PUT_COMMENT, NULL, bva[i].bv_val, bva[i].bv_len ); + } + + ldap_memfree( bva ); + } + + ber_free( ber, 1 ); + + + return 0; +} +#endif + +#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST +static int +print_ppolicy( LDAP *ld, LDAPControl *ctrl ) +{ + int expire = 0, grace = 0, rc; + LDAPPasswordPolicyError pperr; + + rc = ldap_parse_passwordpolicy_control( ld, ctrl, + &expire, &grace, &pperr ); + if ( rc == LDAP_SUCCESS ) { + char buf[ BUFSIZ ], *ptr = buf; + + if ( expire != -1 ) { + ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ), + "expire=%d", expire ); + } + + if ( grace != -1 ) { + ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ), + "%sgrace=%d", ptr == buf ? "" : " ", grace ); + } + + if ( pperr != PP_noError ) { + ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ), + "%serror=%d (%s)", ptr == buf ? "" : " ", + pperr, + ldap_passwordpolicy_err2txt( pperr ) ); + } + + tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE, + ldif ? "ppolicy: " : "ppolicy", buf, ptr - buf ); + } + + return rc; +} +#endif + +#ifdef LDAP_CONTROL_X_PASSWORD_EXPIRED +static int +print_netscape_pwexpired( LDAP *ld, LDAPControl *ctrl ) +{ + printf(_("# PasswordExpired control\n") ); + return 0; +} + +static int +print_netscape_pwexpiring( LDAP *ld, LDAPControl *ctrl ) +{ + long expiring = 0; + int rc; + + rc = ldap_parse_password_expiring_control( ld, ctrl, &expiring ); + if ( rc == LDAP_SUCCESS ) { + printf(_("# PasswordExpiring control seconds=%ld\n"), expiring ); + } + return rc; +} +#endif + +void tool_print_ctrls( + LDAP *ld, + LDAPControl **ctrls ) +{ + int i; + char *ptr; + + for ( i = 0; ctrls[i] != NULL; i++ ) { + /* control: OID criticality base64value */ + struct berval b64 = BER_BVNULL; + ber_len_t len; + char *str; + int j; + + /* FIXME: there might be cases where a control has NULL OID; + * this makes little sense, especially when returned by the + * server, but libldap happily allows it */ + if ( ctrls[i]->ldctl_oid == NULL ) { + continue; + } + + len = ldif ? 2 : 0; + len += strlen( ctrls[i]->ldctl_oid ); + + /* add enough for space after OID and the critical value itself */ + len += ctrls[i]->ldctl_iscritical + ? sizeof("true") : sizeof("false"); + + /* convert to base64 */ + if ( !BER_BVISNULL( &ctrls[i]->ldctl_value ) ) { + b64.bv_len = LUTIL_BASE64_ENCODE_LEN( + ctrls[i]->ldctl_value.bv_len ) + 1; + b64.bv_val = ber_memalloc( b64.bv_len + 1 ); + + b64.bv_len = lutil_b64_ntop( + (unsigned char *) ctrls[i]->ldctl_value.bv_val, + ctrls[i]->ldctl_value.bv_len, + b64.bv_val, b64.bv_len ); + } + + if ( b64.bv_len ) { + len += 1 + b64.bv_len; + } + + ptr = str = malloc( len + 1 ); + if ( ldif ) { + ptr = lutil_strcopy( ptr, ": " ); + } + ptr = lutil_strcopy( ptr, ctrls[i]->ldctl_oid ); + ptr = lutil_strcopy( ptr, ctrls[i]->ldctl_iscritical + ? " true" : " false" ); + + if ( b64.bv_len ) { + ptr = lutil_strcopy( ptr, " " ); + ptr = lutil_strcopy( ptr, b64.bv_val ); + } + + if ( ldif < 2 ) { + tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE, + "control", str, len ); + } + + free( str ); + if ( b64.bv_len ) { + ber_memfree( b64.bv_val ); + } + + /* known controls */ + for ( j = 0; tool_ctrl_response[j].oid != NULL; j++ ) { + if ( strcmp( tool_ctrl_response[j].oid, ctrls[i]->ldctl_oid ) == 0 ) { + if ( !(tool_ctrl_response[j].mask & tool_type )) { + /* this control should not appear + * with this tool; warning? */ + } + break; + } + } + + if ( tool_ctrl_response[j].oid != NULL && tool_ctrl_response[j].func ) { + (void)tool_ctrl_response[j].func( ld, ctrls[i] ); + } + } +} + +int +tool_write_ldif( int type, char *name, char *value, ber_len_t vallen ) +{ + char *ldif; + + if (( ldif = ldif_put_wrap( type, name, value, vallen, ldif_wrap )) == NULL ) { + return( -1 ); + } + + fputs( ldif, stdout ); + ber_memfree( ldif ); + + return( 0 ); +} + +int +tool_is_oid( const char *s ) +{ + int first = 1; + + if ( !isdigit( (unsigned char) s[ 0 ] ) ) { + return 0; + } + + for ( ; s[ 0 ]; s++ ) { + if ( s[ 0 ] == '.' ) { + if ( s[ 1 ] == '\0' ) { + return 0; + } + first = 1; + continue; + } + + if ( !isdigit( (unsigned char) s[ 0 ] ) ) { + return 0; + } + + if ( first == 1 && s[ 0 ] == '0' && s[ 1 ] != '.' ) { + return 0; + } + first = 0; + } + + return 1; +} diff --git a/clients/tools/common.h b/clients/tools/common.h new file mode 100644 index 0000000..116b5d5 --- /dev/null +++ b/clients/tools/common.h @@ -0,0 +1,141 @@ +/* common.h - common definitions for the ldap client tools */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 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>. + */ +/* ACKNOWLEDGEMENTS: + * This file was initially created by Hallvard B. Furuseth based (in + * part) upon argument parsing code for individual tools located in + * this directory. + */ + +#ifndef _CLIENT_TOOLS_COMMON_H_ +#define _CLIENT_TOOLS_COMMON_H_ + +LDAP_BEGIN_DECL + +typedef enum tool_type_t { + TOOL_SEARCH = 0x01U, + TOOL_COMPARE = 0x02U, + TOOL_ADD = 0x04U, + TOOL_DELETE = 0x08U, + TOOL_MODIFY = 0x10U, + TOOL_MODRDN = 0x20U, + + TOOL_EXOP = 0x40U, + + TOOL_WHOAMI = TOOL_EXOP | 0x100U, + TOOL_PASSWD = TOOL_EXOP | 0x200U, + + TOOL_WRITE = (TOOL_ADD|TOOL_DELETE|TOOL_MODIFY|TOOL_MODRDN), + TOOL_READ = (TOOL_SEARCH|TOOL_COMPARE), + + TOOL_ALL = 0xFFU +} tool_type_t; + + +/* input-related vars */ + +/* misc. parameters */ +extern tool_type_t tool_type; +extern int contoper; +extern int debug; +extern char *infile; +extern int dont; +extern int referrals; +extern int verbose; +extern int ldif; +extern ber_len_t ldif_wrap; +extern char *prog; + +/* connection */ +extern char *ldapuri; +extern char *ldaphost; +extern int ldapport; +extern int use_tls; +extern int protocol; +extern int version; + +/* authc/authz */ +extern int authmethod; +extern char *binddn; +extern int want_bindpw; +extern struct berval passwd; +extern char *pw_file; +#ifdef HAVE_CYRUS_SASL +extern unsigned sasl_flags; +extern char *sasl_realm; +extern char *sasl_authc_id; +extern char *sasl_authz_id; +extern char *sasl_mech; +extern char *sasl_secprops; +#endif + +/* controls */ +extern char *assertion; +extern char *authzid; +extern int manageDIT; +extern int manageDSAit; +extern int noop; +extern int ppolicy; +extern int preread, postread; +extern ber_int_t pr_morePagedResults; +extern struct berval pr_cookie; +#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR +extern int chaining; +#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ +extern ber_int_t vlvPos; +extern ber_int_t vlvCount; +extern struct berval *vlvContext; + +/* features */ +extern int backlog; + +/* options */ +extern struct timeval nettimeout; + +/* Defined in common.c, set in main() */ +extern const char __Version[]; + +/* Defined in main program */ +extern const char options[]; + +void usage LDAP_P(( void )) LDAP_GCCATTR((noreturn)); +int handle_private_option LDAP_P(( int i )); + +/* Defined in common.c */ +void tool_init LDAP_P(( tool_type_t type )); +void tool_common_usage LDAP_P(( void )); +void tool_args LDAP_P(( int, char ** )); +LDAP *tool_conn_setup LDAP_P(( int dont, void (*private_setup)( LDAP * ) )); +void tool_bind LDAP_P(( LDAP * )); +void tool_unbind LDAP_P(( LDAP * )); +void tool_destroy LDAP_P(( void )); +void tool_exit LDAP_P(( LDAP *ld, int status )) LDAP_GCCATTR((noreturn)); +void tool_server_controls LDAP_P(( LDAP *, LDAPControl *, int )); +int tool_check_abandon LDAP_P(( LDAP *ld, int msgid )); +void tool_perror LDAP_P(( + const char *func, + int err, + const char *extra, + const char *matched, + const char *info, + char **refs )); +void tool_print_ctrls LDAP_P(( LDAP *ld, LDAPControl **ctrls )); +int tool_write_ldif LDAP_P(( int type, char *name, char *value, ber_len_t vallen )); +int tool_is_oid LDAP_P(( const char *s )); + + +LDAP_END_DECL + +#endif /* _CLIENT_TOOLS_COMMON_H_ */ diff --git a/clients/tools/ldapcompare.c b/clients/tools/ldapcompare.c new file mode 100644 index 0000000..c8c8cf0 --- /dev/null +++ b/clients/tools/ldapcompare.c @@ -0,0 +1,366 @@ +/* ldapcompare.c -- LDAP compare tool */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * Portions Copyright 1998-2003 Kurt D. Zeilenga. + * Portions Copyright 1998-2001 Net Boolean Incorporated. + * 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-1996 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. + */ +/* Portions Copyright 2002, F5 Networks, Inc, All rights reserved. + * This software is not subject to any license of F5 Networks. + * This is free software; you can redistribute and use it + * under the same terms as OpenLDAP itself. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by Jeff Costlow (F5 Networks) + * based, in part, on existing LDAP tools and adapted for inclusion + * into OpenLDAP Software by Kurt D. Zeilenga. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/ctype.h> +#include <ac/string.h> +#include <ac/unistd.h> +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/time.h> +#include <sys/stat.h> + +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_IO_H +#include <io.h> +#endif + +#include <ldap.h> + +#include "lutil.h" +#include "lutil_ldap.h" +#include "ldap_defaults.h" + +#include "common.h" + + +static int quiet = 0; + + +void +usage( void ) +{ + fprintf( stderr, _("usage: %s [options] DN <attr:value|attr::b64value>\n"), prog); + fprintf( stderr, _("where:\n")); + fprintf( stderr, _(" DN\tDistinguished Name\n")); + fprintf( stderr, _(" attr\tassertion attribute\n")); + fprintf( stderr, _(" value\tassertion value\n")); + fprintf( stderr, _(" b64value\tbase64 encoding of assertion value\n")); + + fprintf( stderr, _("Compare options:\n")); + fprintf( stderr, _(" -E [!]<ext>[=<extparam>] compare extensions (! indicates criticality)\n")); + fprintf( stderr, _(" !dontUseCopy (Don't Use Copy)\n")); + fprintf( stderr, _(" -M enable Manage DSA IT control (-MM to make critical)\n")); + fprintf( stderr, _(" -P version protocol version (default: 3)\n")); + fprintf( stderr, _(" -z Quiet mode," + " don't print anything, use return values\n")); + tool_common_usage(); + exit( EXIT_FAILURE ); +} + +static int docompare LDAP_P(( + LDAP *ld, + char *dn, + char *attr, + struct berval *bvalue, + int quiet, + LDAPControl **sctrls, + LDAPControl **cctrls)); + + +const char options[] = "z" + "Cd:D:e:h:H:IMnNO:o:p:P:QR:U:vVw:WxX:y:Y:Z"; + +#ifdef LDAP_CONTROL_DONTUSECOPY +int dontUseCopy = 0; +#endif + +int +handle_private_option( int i ) +{ + char *control, *cvalue; + int crit; + + switch ( i ) { + case 'E': /* compare extensions */ + if( protocol == LDAP_VERSION2 ) { + fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"), + prog, protocol ); + exit( EXIT_FAILURE ); + } + + /* should be extended to support comma separated list of + * [!]key[=value] parameters, e.g. -E !foo,bar=567 + */ + + crit = 0; + cvalue = NULL; + if( optarg[0] == '!' ) { + crit = 1; + optarg++; + } + + control = ber_strdup( optarg ); + if ( (cvalue = strchr( control, '=' )) != NULL ) { + *cvalue++ = '\0'; + } + +#ifdef LDAP_CONTROL_DONTUSECOPY + if ( strcasecmp( control, "dontUseCopy" ) == 0 ) { + if( dontUseCopy ) { + fprintf( stderr, + _("dontUseCopy control previously specified\n")); + exit( EXIT_FAILURE ); + } + if( cvalue != NULL ) { + fprintf( stderr, + _("dontUseCopy: no control value expected\n") ); + usage(); + } + if( !crit ) { + fprintf( stderr, + _("dontUseCopy: critical flag required\n") ); + usage(); + } + + dontUseCopy = 1 + crit; + } else +#endif + { + fprintf( stderr, + _("Invalid compare extension name: %s\n"), control ); + usage(); + } + break; + + case 'z': + quiet = 1; + break; + + default: + return 0; + } + return 1; +} + + +int +main( int argc, char **argv ) +{ + char *compdn = NULL, *attrs = NULL; + char *sep; + int rc; + LDAP *ld = NULL; + struct berval bvalue = { 0, NULL }; + int i = 0; + LDAPControl c[1]; + + + tool_init( TOOL_COMPARE ); + prog = lutil_progname( "ldapcompare", argc, argv ); + + tool_args( argc, argv ); + + if ( argc - optind != 2 ) { + usage(); + } + + compdn = argv[optind++]; + attrs = argv[optind++]; + + /* user passed in only 2 args, the last one better be in + * the form attr:value or attr::b64value + */ + sep = strchr(attrs, ':'); + if (!sep) { + usage(); + } + + *sep++='\0'; + if ( *sep != ':' ) { + bvalue.bv_val = strdup( sep ); + bvalue.bv_len = strlen( bvalue.bv_val ); + + } else { + /* it's base64 encoded. */ + bvalue.bv_val = malloc( strlen( &sep[1] )); + bvalue.bv_len = lutil_b64_pton( &sep[1], + (unsigned char *) bvalue.bv_val, strlen( &sep[1] )); + + if (bvalue.bv_len == (ber_len_t)-1) { + fprintf(stderr, _("base64 decode error\n")); + exit(-1); + } + } + + ld = tool_conn_setup( 0, 0 ); + + tool_bind( ld ); + + if ( 0 +#ifdef LDAP_CONTROL_DONTUSECOPY + || dontUseCopy +#endif + ) + { +#ifdef LDAP_CONTROL_DONTUSECOPY + if ( dontUseCopy ) { + c[i].ldctl_oid = LDAP_CONTROL_DONTUSECOPY; + c[i].ldctl_value.bv_val = NULL; + c[i].ldctl_value.bv_len = 0; + c[i].ldctl_iscritical = dontUseCopy > 1; + i++; + } +#endif + } + + tool_server_controls( ld, c, i ); + + if ( verbose ) { + fprintf( stderr, _("DN:%s, attr:%s, value:%s\n"), + compdn, attrs, sep ); + } + + rc = docompare( ld, compdn, attrs, &bvalue, quiet, NULL, NULL ); + + free( bvalue.bv_val ); + + tool_exit( ld, rc ); +} + + +static int docompare( + LDAP *ld, + char *dn, + char *attr, + struct berval *bvalue, + int quiet, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + int rc, msgid, code; + LDAPMessage *res; + char *matcheddn; + char *text; + char **refs; + LDAPControl **ctrls = NULL; + + if ( dont ) { + return LDAP_SUCCESS; + } + + rc = ldap_compare_ext( ld, dn, attr, bvalue, + sctrls, cctrls, &msgid ); + if ( rc == -1 ) { + return( rc ); + } + + for ( ; ; ) { + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = 100000; + + if ( tool_check_abandon( ld, msgid ) ) { + return LDAP_CANCELLED; + } + + rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res ); + if ( rc < 0 ) { + tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL ); + return rc; + } + + if ( rc != 0 ) { + break; + } + } + + rc = ldap_parse_result( ld, res, &code, &matcheddn, &text, &refs, &ctrls, 1 ); + + if( rc != LDAP_SUCCESS ) { + fprintf( stderr, "%s: ldap_parse_result: %s (%d)\n", + prog, ldap_err2string( rc ), rc ); + return rc; + } + + if ( !quiet && ( verbose || ( code != LDAP_SUCCESS && code != LDAP_COMPARE_TRUE && code != LDAP_COMPARE_FALSE )|| + (matcheddn && *matcheddn) || (text && *text) || (refs && *refs) ) ) + { + printf( _("Compare Result: %s (%d)\n"), + ldap_err2string( code ), code ); + + if( text && *text ) { + printf( _("Additional info: %s\n"), text ); + } + + if( matcheddn && *matcheddn ) { + printf( _("Matched DN: %s\n"), matcheddn ); + } + + if( refs ) { + int i; + for( i=0; refs[i]; i++ ) { + printf(_("Referral: %s\n"), refs[i] ); + } + } + } + + /* if we were told to be quiet, use the return value. */ + if ( !quiet ) { + if ( code == LDAP_COMPARE_TRUE ) { + printf(_("TRUE\n")); + } else if ( code == LDAP_COMPARE_FALSE ) { + printf(_("FALSE\n")); + } else { + printf(_("UNDEFINED\n")); + } + } + + if ( ctrls ) { + tool_print_ctrls( ld, ctrls ); + ldap_controls_free( ctrls ); + } + + ber_memfree( text ); + ber_memfree( matcheddn ); + ber_memvfree( (void **) refs ); + + return( code ); +} + diff --git a/clients/tools/ldapdelete.c b/clients/tools/ldapdelete.c new file mode 100644 index 0000000..0f2183a --- /dev/null +++ b/clients/tools/ldapdelete.c @@ -0,0 +1,443 @@ +/* ldapdelete.c - simple program to delete an entry using LDAP */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 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>. + */ +/* Portions Copyright (c) 1992-1996 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. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). Additional significant contributors + * include: + * Kurt D. Zeilenga + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/ctype.h> +#include <ac/string.h> +#include <ac/unistd.h> +#include <ac/socket.h> +#include <ac/time.h> + +#include <ldap.h> +#include "lutil.h" +#include "lutil_ldap.h" +#include "ldap_defaults.h" + +#include "common.h" + + +static int prune = 0; +static int sizelimit = -1; + + +static int dodelete LDAP_P(( + LDAP *ld, + const char *dn)); + +static int deletechildren LDAP_P(( + LDAP *ld, + const char *dn, + int subentries )); + +void +usage( void ) +{ + fprintf( stderr, _("Delete entries from an LDAP server\n\n")); + fprintf( stderr, _("usage: %s [options] [dn]...\n"), prog); + fprintf( stderr, _(" dn: list of DNs to delete. If not given, it will be readed from stdin\n")); + fprintf( stderr, _(" or from the file specified with \"-f file\".\n")); + fprintf( stderr, _("Delete Options:\n")); + fprintf( stderr, _(" -c continuous operation mode (do not stop on errors)\n")); + fprintf( stderr, _(" -f file read operations from `file'\n")); + fprintf( stderr, _(" -M enable Manage DSA IT control (-MM to make critical)\n")); + fprintf( stderr, _(" -P version protocol version (default: 3)\n")); + fprintf( stderr, _(" -r delete recursively\n")); + tool_common_usage(); + exit( EXIT_FAILURE ); +} + + +const char options[] = "r" + "cd:D:e:f:h:H:IMnNO:o:p:P:QR:U:vVw:WxX:y:Y:z:Z"; + +int +handle_private_option( int i ) +{ + int ival; + char *next; + switch ( i ) { +#if 0 + int crit; + char *control, *cvalue; + case 'E': /* delete extensions */ + if( protocol == LDAP_VERSION2 ) { + fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"), + prog, protocol ); + exit( EXIT_FAILURE ); + } + + /* should be extended to support comma separated list of + * [!]key[=value] parameters, e.g. -E !foo,bar=567 + */ + + crit = 0; + cvalue = NULL; + if( optarg[0] == '!' ) { + crit = 1; + optarg++; + } + + control = strdup( optarg ); + if ( (cvalue = strchr( control, '=' )) != NULL ) { + *cvalue++ = '\0'; + } + fprintf( stderr, _("Invalid delete extension name: %s\n"), control ); + usage(); +#endif + + case 'r': + prune = 1; + break; + + case 'z': /* size limit */ + if ( strcasecmp( optarg, "none" ) == 0 ) { + sizelimit = 0; + + } else if ( strcasecmp( optarg, "max" ) == 0 ) { + sizelimit = LDAP_MAXINT; + + } else { + ival = strtol( optarg, &next, 10 ); + if ( next == NULL || next[0] != '\0' ) { + fprintf( stderr, + _("Unable to parse size limit \"%s\"\n"), optarg ); + exit( EXIT_FAILURE ); + } + sizelimit = ival; + } + if( sizelimit < 0 || sizelimit > LDAP_MAXINT ) { + fprintf( stderr, _("%s: invalid sizelimit (%d) specified\n"), + prog, sizelimit ); + exit( EXIT_FAILURE ); + } + break; + + default: + return 0; + } + return 1; +} + + +static void +private_conn_setup( LDAP *ld ) +{ + /* this seems prudent for searches below */ + int deref = LDAP_DEREF_NEVER; + ldap_set_option( ld, LDAP_OPT_DEREF, &deref ); +} + + +int +main( int argc, char **argv ) +{ + char buf[ 4096 ]; + FILE *fp = NULL; + LDAP *ld; + int rc, retval; + + tool_init( TOOL_DELETE ); + prog = lutil_progname( "ldapdelete", argc, argv ); + + tool_args( argc, argv ); + + if ( infile != NULL ) { + if (( fp = fopen( infile, "r" )) == NULL ) { + perror( optarg ); + exit( EXIT_FAILURE ); + } + } else { + if ( optind >= argc ) { + fp = stdin; + } + } + + ld = tool_conn_setup( 0, &private_conn_setup ); + + tool_bind( ld ); + + tool_server_controls( ld, NULL, 0 ); + + retval = rc = 0; + + if ( fp == NULL ) { + for ( ; optind < argc; ++optind ) { + rc = dodelete( ld, argv[ optind ] ); + + /* Stop on error and no -c option */ + if( rc != 0 ) { + retval = rc; + if( contoper == 0 ) break; + } + } + } else { + while ((rc == 0 || contoper) && fgets(buf, sizeof(buf), fp) != NULL) { + buf[ strlen( buf ) - 1 ] = '\0'; /* remove trailing newline */ + + if ( *buf != '\0' ) { + rc = dodelete( ld, buf ); + if ( rc != 0 ) + retval = rc; + } + } + if ( fp != stdin ) + fclose( fp ); + } + + tool_exit( ld, retval ); +} + + +static int dodelete( + LDAP *ld, + const char *dn) +{ + int id; + int rc, code; + char *matcheddn = NULL, *text = NULL, **refs = NULL; + LDAPControl **ctrls = NULL; + LDAPMessage *res; + int subentries = 0; + + if ( verbose ) { + printf( _("%sdeleting entry \"%s\"\n"), + (dont ? "!" : ""), dn ); + } + + if ( dont ) { + return LDAP_SUCCESS; + } + + /* If prune is on, remove a whole subtree. Delete the children of the + * DN recursively, then the DN requested. + */ + if ( prune ) { +retry:; + deletechildren( ld, dn, subentries ); + } + + rc = ldap_delete_ext( ld, dn, NULL, NULL, &id ); + if ( rc != LDAP_SUCCESS ) { + fprintf( stderr, "%s: ldap_delete_ext: %s (%d)\n", + prog, ldap_err2string( rc ), rc ); + return rc; + } + + for ( ; ; ) { + struct timeval tv; + + if ( tool_check_abandon( ld, id ) ) { + return LDAP_CANCELLED; + } + + tv.tv_sec = 0; + tv.tv_usec = 100000; + + rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res ); + if ( rc < 0 ) { + tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL ); + return rc; + } + + if ( rc != 0 ) { + break; + } + } + + rc = ldap_parse_result( ld, res, &code, &matcheddn, &text, &refs, &ctrls, 1 ); + + switch ( rc ) { + case LDAP_SUCCESS: + break; + + case LDAP_NOT_ALLOWED_ON_NONLEAF: + if ( prune && !subentries ) { + subentries = 1; + goto retry; + } + /* fallthru */ + + default: + fprintf( stderr, "%s: ldap_parse_result: %s (%d)\n", + prog, ldap_err2string( rc ), rc ); + return rc; + } + + if( code != LDAP_SUCCESS ) { + tool_perror( "ldap_delete", code, NULL, matcheddn, text, refs ); + } else if ( verbose && + ((matcheddn && *matcheddn) || (text && *text) || (refs && *refs) )) + { + printf( _("Delete Result: %s (%d)\n"), + ldap_err2string( code ), code ); + + if( text && *text ) { + printf( _("Additional info: %s\n"), text ); + } + + if( matcheddn && *matcheddn ) { + printf( _("Matched DN: %s\n"), matcheddn ); + } + + if( refs ) { + int i; + for( i=0; refs[i]; i++ ) { + printf(_("Referral: %s\n"), refs[i] ); + } + } + } + + if (ctrls) { + tool_print_ctrls( ld, ctrls ); + ldap_controls_free( ctrls ); + } + + ber_memfree( text ); + ber_memfree( matcheddn ); + ber_memvfree( (void **) refs ); + + return code; +} + +/* + * Delete all the children of an entry recursively until leaf nodes are reached. + */ +static int deletechildren( + LDAP *ld, + const char *base, + int subentries ) +{ + LDAPMessage *res, *e; + int entries; + int rc = LDAP_SUCCESS, srch_rc; + static char *attrs[] = { LDAP_NO_ATTRS, NULL }; + LDAPControl c, *ctrls[2], **ctrlsp = NULL; + BerElement *ber = NULL; + + if ( verbose ) printf ( _("deleting children of: %s\n"), base ); + + if ( subentries ) { + /* + * Do a one level search at base for subentry children. + */ + + if ((ber = ber_alloc_t(LBER_USE_DER)) == NULL) { + return EXIT_FAILURE; + } + rc = ber_printf( ber, "b", 1 ); + if ( rc == -1 ) { + ber_free( ber, 1 ); + fprintf( stderr, _("Subentries control encoding error!\n")); + return EXIT_FAILURE; + } + if ( ber_flatten2( ber, &c.ldctl_value, 0 ) == -1 ) { + return EXIT_FAILURE; + } + c.ldctl_oid = LDAP_CONTROL_SUBENTRIES; + c.ldctl_iscritical = 1; + ctrls[0] = &c; + ctrls[1] = NULL; + ctrlsp = ctrls; + } + + /* + * Do a one level search at base for children. For each, delete its children. + */ +more:; + srch_rc = ldap_search_ext_s( ld, base, LDAP_SCOPE_ONELEVEL, NULL, attrs, 1, + ctrlsp, NULL, NULL, sizelimit, &res ); + switch ( srch_rc ) { + case LDAP_SUCCESS: + case LDAP_SIZELIMIT_EXCEEDED: + break; + default: + tool_perror( "ldap_search", srch_rc, NULL, NULL, NULL, NULL ); + return( srch_rc ); + } + + entries = ldap_count_entries( ld, res ); + + if ( entries > 0 ) { + int i; + + for (e = ldap_first_entry( ld, res ), i = 0; e != NULL; + e = ldap_next_entry( ld, e ), i++ ) + { + char *dn = ldap_get_dn( ld, e ); + + if( dn == NULL ) { + ldap_get_option( ld, LDAP_OPT_RESULT_CODE, &rc ); + tool_perror( "ldap_prune", rc, NULL, NULL, NULL, NULL ); + ber_memfree( dn ); + return rc; + } + + rc = deletechildren( ld, dn, 0 ); + if ( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_prune", rc, NULL, NULL, NULL, NULL ); + ber_memfree( dn ); + return rc; + } + + if ( verbose ) { + printf( _("\tremoving %s\n"), dn ); + } + + rc = ldap_delete_ext_s( ld, dn, NULL, NULL ); + if ( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_delete", rc, NULL, NULL, NULL, NULL ); + ber_memfree( dn ); + return rc; + + } + + if ( verbose ) { + printf( _("\t%s removed\n"), dn ); + } + + ber_memfree( dn ); + } + } + + ldap_msgfree( res ); + + if ( srch_rc == LDAP_SIZELIMIT_EXCEEDED ) { + goto more; + } + + return rc; +} diff --git a/clients/tools/ldapexop.c b/clients/tools/ldapexop.c new file mode 100644 index 0000000..fb719cf --- /dev/null +++ b/clients/tools/ldapexop.c @@ -0,0 +1,355 @@ +/* ldapexop.c -- a tool for performing well-known extended operations */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2005-2021 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>. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by Pierangelo Masarati for inclusion + * in OpenLDAP Software based, in part, on other client tools. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/ctype.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/unistd.h> + +#include <ldap.h> +#include "ldif.h" +#include "lutil.h" +#include "lutil_ldap.h" +#include "ldap_defaults.h" + +#include "common.h" + +void +usage( void ) +{ + fprintf( stderr, _("Issue LDAP extended operations\n\n")); + fprintf( stderr, _("usage: %s [options] <oid|oid:data|oid::b64data>\n"), prog); + fprintf( stderr, _(" %s [options] whoami\n"), prog); + fprintf( stderr, _(" %s [options] cancel <id>\n"), prog); + fprintf( stderr, _(" %s [options] refresh <DN> [<ttl>]\n"), prog); + tool_common_usage(); + exit( EXIT_FAILURE ); +} + + +const char options[] = "" + "d:D:e:h:H:InNO:o:p:QR:U:vVw:WxX:y:Y:Z"; + +int +handle_private_option( int i ) +{ + switch ( i ) { + default: + return 0; + } + return 1; +} + + +int +main( int argc, char *argv[] ) +{ + int rc; + + LDAP *ld = NULL; + + char *matcheddn = NULL, *text = NULL, **refs = NULL; + LDAPControl **ctrls = NULL; + int id, code; + LDAPMessage *res = NULL; + + tool_init( TOOL_EXOP ); + prog = lutil_progname( "ldapexop", argc, argv ); + + /* LDAPv3 only */ + protocol = LDAP_VERSION3; + + tool_args( argc, argv ); + + if ( argc - optind < 1 ) { + usage(); + } + + ld = tool_conn_setup( 0, 0 ); + + tool_bind( ld ); + + argv += optind; + argc -= optind; + + if ( strcasecmp( argv[ 0 ], "whoami" ) == 0 ) { + tool_server_controls( ld, NULL, 0 ); + + rc = ldap_whoami( ld, NULL, NULL, &id ); + if ( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_extended_operation", rc, NULL, NULL, NULL, NULL ); + rc = EXIT_FAILURE; + goto skip; + } + + } else if ( strcasecmp( argv[ 0 ], "cancel" ) == 0 ) { + int cancelid; + + switch ( argc ) { + case 2: + if ( lutil_atoi( &cancelid, argv[ 1 ] ) != 0 || cancelid < 0 ) { + fprintf( stderr, "invalid cancelid=%s\n\n", argv[ 1 ] ); + usage(); + } + break; + + default: + fprintf( stderr, "need cancelid\n\n" ); + usage(); + } + + rc = ldap_cancel( ld, cancelid, NULL, NULL, &id ); + if ( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_cancel", rc, NULL, NULL, NULL, NULL ); + rc = EXIT_FAILURE; + goto skip; + } + + } else if ( strcasecmp( argv[ 0 ], "passwd" ) == 0 ) { + fprintf( stderr, "use ldappasswd(1) instead.\n\n" ); + usage(); + /* TODO? */ + + } else if ( strcasecmp( argv[ 0 ], "refresh" ) == 0 ) { + int ttl = 3600; + struct berval dn; + + switch ( argc ) { + case 3: + ttl = atoi( argv[ 2 ] ); + + case 2: + dn.bv_val = argv[ 1 ]; + dn.bv_len = strlen( dn.bv_val ); + break; + + default: + fprintf( stderr, _("need DN [ttl]\n\n") ); + usage(); + } + + tool_server_controls( ld, NULL, 0 ); + + rc = ldap_refresh( ld, &dn, ttl, NULL, NULL, &id ); + if ( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_extended_operation", rc, NULL, NULL, NULL, NULL ); + rc = EXIT_FAILURE; + goto skip; + } + + } else { + char *p; + + if ( argc != 1 ) { + usage(); + } + + p = strchr( argv[ 0 ], ':' ); + if ( p == argv[ 0 ] ) { + usage(); + } + + if ( p != NULL ) + *p++ = '\0'; + + if ( tool_is_oid( argv[ 0 ] ) ) { + struct berval reqdata; + struct berval type; + struct berval value; + int freeval; + + if ( p != NULL ) { + p[ -1 ] = ':'; + ldif_parse_line2( argv[ 0 ], &type, &value, &freeval ); + p[ -1 ] = '\0'; + + if ( freeval ) { + reqdata = value; + } else { + ber_dupbv( &reqdata, &value ); + } + } + + + tool_server_controls( ld, NULL, 0 ); + + rc = ldap_extended_operation( ld, argv[ 0 ], p ? &reqdata : NULL, NULL, NULL, &id ); + if ( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_extended_operation", rc, NULL, NULL, NULL, NULL ); + rc = EXIT_FAILURE; + goto skip; + } + } else { + fprintf( stderr, "unknown exop \"%s\"\n\n", argv[ 0 ] ); + usage(); + } + } + + for ( ; ; ) { + struct timeval tv; + + if ( tool_check_abandon( ld, id ) ) { + tool_exit( ld, LDAP_CANCELLED ); + } + + tv.tv_sec = 0; + tv.tv_usec = 100000; + + rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res ); + if ( rc < 0 ) { + tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL ); + rc = EXIT_FAILURE; + goto skip; + } + + if ( rc != 0 ) { + break; + } + } + + rc = ldap_parse_result( ld, res, + &code, &matcheddn, &text, &refs, &ctrls, 0 ); + if ( rc == LDAP_SUCCESS ) { + rc = code; + } + + if ( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_parse_result", rc, NULL, matcheddn, text, refs ); + rc = EXIT_FAILURE; + goto skip; + } + + if ( strcasecmp( argv[ 0 ], "whoami" ) == 0 ) { + char *retoid = NULL; + struct berval *retdata = NULL; + + rc = ldap_parse_extended_result( ld, res, &retoid, &retdata, 0 ); + + if ( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_parse_extended_result", rc, NULL, NULL, NULL, NULL ); + rc = EXIT_FAILURE; + goto skip; + } + + if ( retdata != NULL ) { + if ( retdata->bv_len == 0 ) { + printf(_("anonymous\n") ); + } else { + printf("%s\n", retdata->bv_val ); + } + } + + ber_memfree( retoid ); + ber_bvfree( retdata ); + + } else if ( strcasecmp( argv[ 0 ], "cancel" ) == 0 ) { + /* no extended response; returns specific errors */ + assert( 0 ); + + } else if ( strcasecmp( argv[ 0 ], "passwd" ) == 0 ) { + /* TODO */ + + } else if ( strcasecmp( argv[ 0 ], "refresh" ) == 0 ) { + int newttl; + + rc = ldap_parse_refresh( ld, res, &newttl ); + + if ( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_parse_refresh", rc, NULL, NULL, NULL, NULL ); + rc = EXIT_FAILURE; + goto skip; + } + + printf( "newttl=%d\n", newttl ); + + } else if ( tool_is_oid( argv[ 0 ] ) ) { + char *retoid = NULL; + struct berval *retdata = NULL; + + if( ldif < 2 ) { + printf(_("# extended operation response\n")); + } + + rc = ldap_parse_extended_result( ld, res, &retoid, &retdata, 0 ); + if ( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_parse_extended_result", rc, NULL, NULL, NULL, NULL ); + rc = EXIT_FAILURE; + goto skip; + } + + if ( ldif < 2 && retoid != NULL ) { + tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE, + "oid", retoid, strlen(retoid) ); + } + + ber_memfree( retoid ); + + if( retdata != NULL ) { + if ( ldif < 2 ) { + tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_BINARY, + "data", retdata->bv_val, retdata->bv_len ); + } + + ber_bvfree( retdata ); + } + } + + if( verbose || code != LDAP_SUCCESS || + ( matcheddn && *matcheddn ) || ( text && *text ) || refs ) { + printf( _("Result: %s (%d)\n"), ldap_err2string( code ), code ); + + if( text && *text ) { + printf( _("Additional info: %s\n"), text ); + } + + if( matcheddn && *matcheddn ) { + printf( _("Matched DN: %s\n"), matcheddn ); + } + + if( refs ) { + int i; + for( i=0; refs[i]; i++ ) { + printf(_("Referral: %s\n"), refs[i] ); + } + } + } + + if (ctrls) { + tool_print_ctrls( ld, ctrls ); + ldap_controls_free( ctrls ); + } + + ber_memfree( text ); + ber_memfree( matcheddn ); + ber_memvfree( (void **) refs ); + +skip: + /* disconnect from server */ + if ( res ) + ldap_msgfree( res ); + tool_exit( ld, rc ); +} diff --git a/clients/tools/ldapmodify.c b/clients/tools/ldapmodify.c new file mode 100644 index 0000000..92a01cf --- /dev/null +++ b/clients/tools/ldapmodify.c @@ -0,0 +1,1282 @@ +/* ldapmodify.c - generic program to modify or add entries using LDAP */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * Portions Copyright 2006 Howard Chu. + * Portions Copyright 1998-2003 Kurt D. Zeilenga. + * Portions Copyright 1998-2001 Net Boolean Incorporated. + * Portions Copyright 2001-2003 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) 1992-1996 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. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). Additional significant contributors + * include: + * Kurt D. Zeilenga + * Norbert Klasen + * Howard Chu + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/ctype.h> +#include <ac/string.h> +#include <ac/unistd.h> +#include <ac/socket.h> +#include <ac/time.h> + +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +#ifdef HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#include <ldap.h> + +#include "lutil.h" +#include "lutil_ldap.h" +#include "ldif.h" +#include "ldap_defaults.h" +#include "ldap_pvt.h" +#include "lber_pvt.h" + +#include "common.h" + +static int ldapadd; +static char *rejfile = NULL; +static LDAP *ld = NULL; + +#define M_SEP 0x7f + +/* strings found in LDIF entries */ +static struct berval BV_VERSION = BER_BVC("version"); +static struct berval BV_DN = BER_BVC("dn"); +static struct berval BV_CONTROL = BER_BVC("control"); +static struct berval BV_CHANGETYPE = BER_BVC("changetype"); +static struct berval BV_ADDCT = BER_BVC("add"); +static struct berval BV_MODIFYCT = BER_BVC("modify"); +static struct berval BV_DELETECT = BER_BVC("delete"); +static struct berval BV_MODRDNCT = BER_BVC("modrdn"); +static struct berval BV_MODDNCT = BER_BVC("moddn"); +static struct berval BV_RENAMECT = BER_BVC("rename"); +static struct berval BV_MODOPADD = BER_BVC("add"); +static struct berval BV_MODOPREPLACE = BER_BVC("replace"); +static struct berval BV_MODOPDELETE = BER_BVC("delete"); +static struct berval BV_MODOPINCREMENT = BER_BVC("increment"); +static struct berval BV_NEWRDN = BER_BVC("newrdn"); +static struct berval BV_DELETEOLDRDN = BER_BVC("deleteoldrdn"); +static struct berval BV_NEWSUP = BER_BVC("newsuperior"); + +#define BV_CASEMATCH(a, b) \ + ((a)->bv_len == (b)->bv_len && 0 == strcasecmp((a)->bv_val, (b)->bv_val)) + +static int process_ldif_rec LDAP_P(( char *rbuf, unsigned long lineno )); +static int parse_ldif_control LDAP_P(( struct berval *val, LDAPControl ***pctrls )); +static int domodify LDAP_P(( + const char *dn, + LDAPMod **pmods, + LDAPControl **pctrls, + int newentry )); +static int dodelete LDAP_P(( + const char *dn, + LDAPControl **pctrls )); +static int dorename LDAP_P(( + const char *dn, + const char *newrdn, + const char *newsup, + int deleteoldrdn, + LDAPControl **pctrls )); +static int process_response( + LDAP *ld, + int msgid, + int res, + const char *dn ); + +#ifdef LDAP_X_TXN +static int txn = 0; +static int txnabort = 0; +struct berval *txn_id = NULL; +#endif + +void +usage( void ) +{ + fprintf( stderr, _("Add or modify entries from an LDAP server\n\n")); + fprintf( stderr, _("usage: %s [options]\n"), prog); + fprintf( stderr, _(" The list of desired operations are read from stdin" + " or from the file\n")); + fprintf( stderr, _(" specified by \"-f file\".\n")); + fprintf( stderr, _("Add or modify options:\n")); + fprintf( stderr, _(" -a add values (%s)\n"), + (ldapadd ? _("default") : _("default is to replace"))); + fprintf( stderr, _(" -c continuous operation mode (do not stop on errors)\n")); + fprintf( stderr, _(" -E [!]ext=extparam modify extensions" + " (! indicate s criticality)\n")); + fprintf( stderr, _(" -f file read operations from `file'\n")); + fprintf( stderr, _(" -M enable Manage DSA IT control (-MM to make critical)\n")); + fprintf( stderr, _(" -P version protocol version (default: 3)\n")); +#ifdef LDAP_X_TXN + fprintf( stderr, + _(" [!]txn=<commit|abort> (transaction)\n")); +#endif + fprintf( stderr, _(" -S file write skipped modifications to `file'\n")); + + tool_common_usage(); + exit( EXIT_FAILURE ); +} + + +const char options[] = "aE:rS:" + "cd:D:e:f:h:H:IMnNO:o:p:P:QR:U:vVw:WxX:y:Y:Z"; + +int +handle_private_option( int i ) +{ + char *control, *cvalue; + int crit; + + switch ( i ) { + case 'E': /* modify extensions */ + if( protocol == LDAP_VERSION2 ) { + fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"), + prog, protocol ); + exit( EXIT_FAILURE ); + } + + /* should be extended to support comma separated list of + * [!]key[=value] parameters, e.g. -E !foo,bar=567 + */ + + crit = 0; + cvalue = NULL; + if( optarg[0] == '!' ) { + crit = 1; + optarg++; + } + + control = ber_strdup( optarg ); + if ( (cvalue = strchr( control, '=' )) != NULL ) { + *cvalue++ = '\0'; + } + +#ifdef LDAP_X_TXN + if( strcasecmp( control, "txn" ) == 0 ) { + /* Transaction */ + if( txn ) { + fprintf( stderr, + _("txn control previously specified\n")); + exit( EXIT_FAILURE ); + } + if( cvalue != NULL ) { + if( strcasecmp( cvalue, "abort" ) == 0 ) { + txnabort=1; + } else if( strcasecmp( cvalue, "commit" ) != 0 ) { + fprintf( stderr, _("Invalid value for txn control, %s\n"), + cvalue ); + exit( EXIT_FAILURE ); + } + } + + txn = 1 + crit; + } else +#endif + { + fprintf( stderr, _("Invalid modify extension name: %s\n"), + control ); + usage(); + } + break; + + case 'a': /* add */ + ldapadd = 1; + break; + + case 'r': /* replace (obsolete) */ + break; + + case 'S': /* skipped modifications to file */ + if( rejfile != NULL ) { + fprintf( stderr, _("%s: -S previously specified\n"), prog ); + exit( EXIT_FAILURE ); + } + rejfile = ber_strdup( optarg ); + break; + + default: + return 0; + } + return 1; +} + + +int +main( int argc, char **argv ) +{ + char *rbuf = NULL, *rejbuf = NULL; + FILE *rejfp; + struct LDIFFP *ldiffp = NULL, ldifdummy = {0}; + char *matched_msg, *error_msg; + int rc, retval, ldifrc; + int len; + int i = 0, lmax = 0; + unsigned long lineno, nextline = 0; + LDAPControl c[1]; + + prog = lutil_progname( "ldapmodify", argc, argv ); + + /* strncmp instead of strcmp since NT binaries carry .exe extension */ + ldapadd = ( strncasecmp( prog, "ldapadd", sizeof("ldapadd")-1 ) == 0 ); + + tool_init( ldapadd ? TOOL_ADD : TOOL_MODIFY ); + + tool_args( argc, argv ); + + if ( argc != optind ) usage(); + + if ( rejfile != NULL ) { + if (( rejfp = fopen( rejfile, "w" )) == NULL ) { + perror( rejfile ); + retval = EXIT_FAILURE; + goto fail; + } + } else { + rejfp = NULL; + } + + if ( infile != NULL ) { + if (( ldiffp = ldif_open( infile, "r" )) == NULL ) { + perror( infile ); + retval = EXIT_FAILURE; + goto fail; + } + } else { + ldifdummy.fp = stdin; + ldiffp = &ldifdummy; + } + + if ( debug ) ldif_debug = debug; + + ld = tool_conn_setup( dont, 0 ); + + if ( !dont ) { + tool_bind( ld ); + } + +#ifdef LDAP_X_TXN + if( txn ) { + /* start transaction */ + rc = ldap_txn_start_s( ld, NULL, NULL, &txn_id ); + if( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_txn_start_s", rc, NULL, NULL, NULL, NULL ); + if( txn > 1 ) { + retval = EXIT_FAILURE; + goto fail; + } + txn = 0; + } + } +#endif + + if ( 0 +#ifdef LDAP_X_TXN + || txn +#endif + ) + { +#ifdef LDAP_X_TXN + if( txn ) { + c[i].ldctl_oid = LDAP_CONTROL_X_TXN_SPEC; + c[i].ldctl_value = *txn_id; + c[i].ldctl_iscritical = 1; + i++; + } +#endif + } + + tool_server_controls( ld, c, i ); + + rc = 0; + retval = 0; + lineno = 1; + while (( rc == 0 || contoper ) && ( ldifrc = ldif_read_record( ldiffp, &nextline, + &rbuf, &lmax )) > 0 ) + { + if ( rejfp ) { + len = strlen( rbuf ); + if (( rejbuf = (char *)ber_memalloc( len+1 )) == NULL ) { + perror( "malloc" ); + retval = EXIT_FAILURE; + goto fail; + } + memcpy( rejbuf, rbuf, len+1 ); + } + + rc = process_ldif_rec( rbuf, lineno ); + lineno = nextline+1; + + if ( rc ) retval = rc; + if ( rc && rejfp ) { + fprintf(rejfp, _("# Error: %s (%d)"), ldap_err2string(rc), rc); + + matched_msg = NULL; + ldap_get_option(ld, LDAP_OPT_MATCHED_DN, &matched_msg); + if ( matched_msg != NULL ) { + if ( *matched_msg != '\0' ) { + fprintf( rejfp, _(", matched DN: %s"), matched_msg ); + } + ldap_memfree( matched_msg ); + } + + error_msg = NULL; + ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, &error_msg); + if ( error_msg != NULL ) { + if ( *error_msg != '\0' ) { + fprintf( rejfp, _(", additional info: %s"), error_msg ); + } + ldap_memfree( error_msg ); + } + fprintf( rejfp, "\n%s\n", rejbuf ); + } + + if (rejfp) ber_memfree( rejbuf ); + } + ber_memfree( rbuf ); + + if ( ldifrc < 0 ) + retval = LDAP_OTHER; + +#ifdef LDAP_X_TXN + if( retval == 0 && txn ) { + rc = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, NULL ); + if ( rc != LDAP_OPT_SUCCESS ) { + fprintf( stderr, "Could not unset controls for ldap_txn_end\n"); + } + + /* create transaction */ + rc = ldap_txn_end_s( ld, !txnabort, txn_id, NULL, NULL, NULL ); + if( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_txn_end_s", rc, NULL, NULL, NULL, NULL ); + retval = rc; + } + } +#endif + +fail:; + if ( rejfp != NULL ) { + fclose( rejfp ); + } + + if ( ldiffp != NULL && ldiffp != &ldifdummy ) { + ldif_close( ldiffp ); + } + + tool_exit( ld, retval ); +} + + +static int +process_ldif_rec( char *rbuf, unsigned long linenum ) +{ + char *line, *dn, *newrdn, *newsup; + int rc, modop; + int expect_modop, expect_sep; + int deleteoldrdn; + int new_entry, delete_entry, got_all; + LDAPMod **pmods, *lm = NULL; + int version; + LDAPControl **pctrls; + int i, j, k, lines, idn, nmods; + struct berval *btype, *vals, **bvl, bv; + char *freeval; + unsigned char *mops = NULL; + + new_entry = ldapadd; + + rc = got_all = delete_entry = modop = expect_modop = 0; + expect_sep = 0; + version = 0; + deleteoldrdn = 1; + pmods = NULL; + pctrls = NULL; + dn = newrdn = newsup = NULL; + + lines = ldif_countlines( rbuf ); + btype = ber_memcalloc( 1, (lines+1)*2*sizeof(struct berval)+lines ); + if ( !btype ) + return LDAP_NO_MEMORY; + + vals = btype+lines+1; + freeval = (char *)(vals+lines+1); + i = -1; + + while ( rc == 0 && ( line = ldif_getline( &rbuf )) != NULL ) { + int freev; + + if ( *line == '\n' || *line == '\0' ) { + break; + } + + ++i; + + if ( line[0] == '-' && !line[1] ) { + BER_BVZERO( btype+i ); + freeval[i] = 0; + continue; + } + + if ( ( rc = ldif_parse_line2( line, btype+i, vals+i, &freev ) ) < 0 ) { + fprintf( stderr, _("%s: invalid format (line %lu) entry: \"%s\"\n"), + prog, linenum+i, dn == NULL ? "" : dn ); + rc = LDAP_PARAM_ERROR; + goto leave; + } + freeval[i] = freev; + + if ( dn == NULL ) { + if ( linenum+i == 1 && BV_CASEMATCH( btype+i, &BV_VERSION )) { + int v; + if( vals[i].bv_len == 0 || lutil_atoi( &v, vals[i].bv_val) != 0 || v != 1 ) { + fprintf( stderr, + _("%s: invalid version %s, line %lu (ignored)\n"), + prog, vals[i].bv_val, linenum ); + } + version++; + + } else if ( BV_CASEMATCH( btype+i, &BV_DN )) { + dn = vals[i].bv_val; + idn = i; + } + /* skip all lines until we see "dn:" */ + } + } + + /* check to make sure there was a dn: line */ + if ( !dn ) { + rc = 0; + goto leave; + } + + lines = i+1; + + if( lines == 0 ) { + rc = 0; + goto leave; + } + + if( version && lines == 1 ) { + rc = 0; + goto leave; + } + + i = idn+1; + /* Check for "control" tag after dn and before changetype. */ + if ( BV_CASEMATCH( btype+i, &BV_CONTROL )) { + /* Parse and add it to the list of controls */ + rc = parse_ldif_control( vals+i, &pctrls ); + if (rc != 0) { + fprintf( stderr, + _("%s: Error processing %s line, line %lu: %s\n"), + prog, BV_CONTROL.bv_val, linenum+i, ldap_err2string(rc) ); + } + i++; + if ( i>= lines ) { +short_input: + fprintf( stderr, + _("%s: Expecting more input after %s line, line %lu\n"), + prog, btype[i-1].bv_val, linenum+i ); + + rc = LDAP_PARAM_ERROR; + goto leave; + } + } + + /* Check for changetype */ + if ( BV_CASEMATCH( btype+i, &BV_CHANGETYPE )) { +#ifdef LIBERAL_CHANGETYPE_MODOP + /* trim trailing spaces (and log warning ...) */ + int icnt; + for ( icnt = vals[i].bv_len; --icnt > 0; ) { + if ( !isspace( (unsigned char) vals[i].bv_val[icnt] ) ) { + break; + } + } + + if ( ++icnt != vals[i].bv_len ) { + fprintf( stderr, _("%s: illegal trailing space after" + " \"%s: %s\" trimmed (line %lu, entry \"%s\")\n"), + prog, BV_CHANGETYPE.bv_val, vals[i].bv_val, linenum+i, dn ); + vals[i].bv_val[icnt] = '\0'; + } +#endif /* LIBERAL_CHANGETYPE_MODOP */ + + if ( BV_CASEMATCH( vals+i, &BV_MODIFYCT )) { + new_entry = 0; + expect_modop = 1; + } else if ( BV_CASEMATCH( vals+i, &BV_ADDCT )) { + new_entry = 1; + modop = LDAP_MOD_ADD; + } else if ( BV_CASEMATCH( vals+i, &BV_MODRDNCT ) + || BV_CASEMATCH( vals+i, &BV_MODDNCT ) + || BV_CASEMATCH( vals+i, &BV_RENAMECT )) + { + i++; + if ( i >= lines ) + goto short_input; + if ( !BV_CASEMATCH( btype+i, &BV_NEWRDN )) { + fprintf( stderr, _("%s: expecting \"%s:\" but saw" + " \"%s:\" (line %lu, entry \"%s\")\n"), + prog, BV_NEWRDN.bv_val, btype[i].bv_val, linenum+i, dn ); + rc = LDAP_PARAM_ERROR; + goto leave; + } + newrdn = vals[i].bv_val; + i++; + if ( i >= lines ) + goto short_input; + if ( !BV_CASEMATCH( btype+i, &BV_DELETEOLDRDN )) { + fprintf( stderr, _("%s: expecting \"%s:\" but saw" + " \"%s:\" (line %lu, entry \"%s\")\n"), + prog, BV_DELETEOLDRDN.bv_val, btype[i].bv_val, linenum+i, dn ); + rc = LDAP_PARAM_ERROR; + goto leave; + } + deleteoldrdn = ( vals[i].bv_val[0] == '0' ) ? 0 : 1; + i++; + if ( i < lines ) { + if ( !BV_CASEMATCH( btype+i, &BV_NEWSUP )) { + fprintf( stderr, _("%s: expecting \"%s:\" but saw" + " \"%s:\" (line %lu, entry \"%s\")\n"), + prog, BV_NEWSUP.bv_val, btype[i].bv_val, linenum+i, dn ); + rc = LDAP_PARAM_ERROR; + goto leave; + } + newsup = vals[i].bv_val; + i++; + } + got_all = 1; + } else if ( BV_CASEMATCH( vals+i, &BV_DELETECT )) { + got_all = delete_entry = 1; + } else { + fprintf( stderr, + _("%s: unknown %s \"%s\" (line %lu, entry \"%s\")\n"), + prog, BV_CHANGETYPE.bv_val, vals[i].bv_val, linenum+i, dn ); + rc = LDAP_PARAM_ERROR; + goto leave; + } + i++; + } else if ( ldapadd ) { /* missing changetype => add */ + new_entry = 1; + modop = LDAP_MOD_ADD; + } else { + expect_modop = 1; /* missing changetype => modify */ + } + + if ( got_all ) { + if ( i < lines ) { + fprintf( stderr, + _("%s: extra lines at end (line %lu, entry \"%s\")\n"), + prog, linenum+i, dn ); + rc = LDAP_PARAM_ERROR; + goto leave; + } + goto doit; + } + + nmods = lines - i; + idn = i; + + if ( new_entry ) { + int fv; + + /* Make sure all attributes with multiple values are contiguous */ + for (; i<lines; i++) { + for (j=i+1; j<lines; j++) { + if ( !btype[j].bv_val ) { + fprintf( stderr, + _("%s: missing attributeDescription (line %lu, entry \"%s\")\n"), + prog, linenum+j, dn ); + rc = LDAP_PARAM_ERROR; + goto leave; + } + if ( BV_CASEMATCH( btype+i, btype+j )) { + nmods--; + /* out of order, move intervening attributes down */ + if ( j != i+1 ) { + bv = vals[j]; + fv = freeval[j]; + for (k=j; k>i; k--) { + btype[k] = btype[k-1]; + vals[k] = vals[k-1]; + freeval[k] = freeval[k-1]; + } + k++; + btype[k] = btype[i]; + vals[k] = bv; + freeval[k] = fv; + } + i++; + } + } + } + /* Allocate space for array of mods, array of pointers to mods, + * and array of pointers to values, allowing for NULL terminators + * for the pointer arrays... + */ + lm = ber_memalloc( nmods * sizeof(LDAPMod) + + (nmods+1) * sizeof(LDAPMod*) + + (lines + nmods - idn) * sizeof(struct berval *)); + pmods = (LDAPMod **)(lm+nmods); + bvl = (struct berval **)(pmods+nmods+1); + + j = 0; + k = -1; + BER_BVZERO(&bv); + for (i=idn; i<lines; i++) { + if ( BV_CASEMATCH( btype+i, &BV_DN )) { + fprintf( stderr, _("%s: attributeDescription \"%s\":" + " (possible missing newline" + " after line %lu, entry \"%s\"?)\n"), + prog, btype[i].bv_val, linenum+i - 1, dn ); + } + if ( !BV_CASEMATCH( btype+i, &bv )) { + bvl[k++] = NULL; + bv = btype[i]; + lm[j].mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES; + lm[j].mod_type = bv.bv_val; + lm[j].mod_bvalues = bvl+k; + pmods[j] = lm+j; + j++; + } + bvl[k++] = vals+i; + } + bvl[k] = NULL; + pmods[j] = NULL; + goto doit; + } + + mops = ber_memalloc( lines+1 ); + mops[lines] = M_SEP; + mops[i-1] = M_SEP; + + for ( ; i<lines; i++ ) { + if ( expect_modop ) { +#ifdef LIBERAL_CHANGETYPE_MODOP + /* trim trailing spaces (and log warning ...) */ + int icnt; + for ( icnt = vals[i].bv_len; --icnt > 0; ) { + if ( !isspace( (unsigned char) vals[i].bv_val[icnt] ) ) break; + } + + if ( ++icnt != vals[i].bv_len ) { + fprintf( stderr, _("%s: illegal trailing space after" + " \"%s: %s\" trimmed (line %lu, entry \"%s\")\n"), + prog, type, vals[i].bv_val, linenum+i, dn ); + vals[i].bv_val[icnt] = '\0'; + } +#endif /* LIBERAL_CHANGETYPE_MODOP */ + + expect_modop = 0; + expect_sep = 1; + if ( BV_CASEMATCH( btype+i, &BV_MODOPADD )) { + modop = LDAP_MOD_ADD; + mops[i] = M_SEP; + nmods--; + } else if ( BV_CASEMATCH( btype+i, &BV_MODOPREPLACE )) { + /* defer handling these since they might have no values. + * Use the BVALUES flag to signal that these were + * deferred. If values are provided later, this + * flag will be switched off. + */ + modop = LDAP_MOD_REPLACE; + mops[i] = modop | LDAP_MOD_BVALUES; + btype[i] = vals[i]; + } else if ( BV_CASEMATCH( btype+i, &BV_MODOPDELETE )) { + modop = LDAP_MOD_DELETE; + mops[i] = modop | LDAP_MOD_BVALUES; + btype[i] = vals[i]; + } else if ( BV_CASEMATCH( btype+i, &BV_MODOPINCREMENT )) { + modop = LDAP_MOD_INCREMENT; + mops[i] = M_SEP; + nmods--; + } else { /* no modify op: invalid LDIF */ + fprintf( stderr, _("%s: modify operation type is missing at" + " line %lu, entry \"%s\"\n"), + prog, linenum+i, dn ); + rc = LDAP_PARAM_ERROR; + goto leave; + } + bv = vals[i]; + } else if ( expect_sep && BER_BVISEMPTY( btype+i )) { + mops[i] = M_SEP; + expect_sep = 0; + expect_modop = 1; + nmods--; + } else { + if ( !BV_CASEMATCH( btype+i, &bv )) { + fprintf( stderr, _("%s: wrong attributeType at" + " line %lu, entry \"%s\"\n"), + prog, linenum+i, dn ); + rc = LDAP_PARAM_ERROR; + goto leave; + } + mops[i] = modop; + /* If prev op was deferred and matches this type, + * clear the flag + */ + if ( (mops[i-1] & LDAP_MOD_BVALUES) + && BV_CASEMATCH( btype+i, btype+i-1 )) + { + mops[i-1] = M_SEP; + nmods--; + } + } + } + +#if 0 /* we should faithfully encode the LDIF, not combine */ + /* Make sure all modops with multiple values are contiguous */ + for (i=idn; i<lines; i++) { + if ( mops[i] == M_SEP ) + continue; + for (j=i+1; j<lines; j++) { + if ( mops[j] == M_SEP || mops[i] != mops[j] ) + continue; + if ( BV_CASEMATCH( btype+i, btype+j )) { + nmods--; + /* out of order, move intervening attributes down */ + if ( j != i+1 ) { + int c; + struct berval bv; + char fv; + + c = mops[j]; + bv = vals[j]; + fv = freeval[j]; + for (k=j; k>i; k--) { + btype[k] = btype[k-1]; + vals[k] = vals[k-1]; + freeval[k] = freeval[k-1]; + mops[k] = mops[k-1]; + } + k++; + btype[k] = btype[i]; + vals[k] = bv; + freeval[k] = fv; + mops[k] = c; + } + i++; + } + } + } +#endif + + /* Allocate space for array of mods, array of pointers to mods, + * and array of pointers to values, allowing for NULL terminators + * for the pointer arrays... + */ + lm = ber_memalloc( nmods * sizeof(LDAPMod) + + (nmods+1) * sizeof(LDAPMod*) + + (lines + nmods - idn) * sizeof(struct berval *)); + pmods = (LDAPMod **)(lm+nmods); + bvl = (struct berval **)(pmods+nmods+1); + + j = 0; + k = -1; + BER_BVZERO(&bv); + mops[idn-1] = M_SEP; + for (i=idn; i<lines; i++) { + if ( mops[i] == M_SEP ) + continue; + if ( mops[i] != mops[i-1] || !BV_CASEMATCH( btype+i, &bv )) { + bvl[k++] = NULL; + bv = btype[i]; + lm[j].mod_op = mops[i] | LDAP_MOD_BVALUES; + lm[j].mod_type = bv.bv_val; + if ( mops[i] & LDAP_MOD_BVALUES ) { + lm[j].mod_bvalues = NULL; + } else { + lm[j].mod_bvalues = bvl+k; + } + pmods[j] = lm+j; + j++; + } + bvl[k++] = vals+i; + } + bvl[k] = NULL; + pmods[j] = NULL; + +doit: + /* If default controls are set (as with -M option) and controls are + specified in the LDIF file, we must add the default controls to + the list of controls sent with the ldap operation. + */ + if ( rc == 0 ) { + if (pctrls) { + LDAPControl **defctrls = NULL; /* Default server controls */ + LDAPControl **newctrls = NULL; + ldap_get_option(ld, LDAP_OPT_SERVER_CONTROLS, &defctrls); + if (defctrls) { + int npc=0; /* Num of LDIF controls */ + int ndefc=0; /* Num of default controls */ + while (pctrls[npc]) npc++; /* Count LDIF controls */ + while (defctrls[ndefc]) ndefc++; /* Count default controls */ + newctrls = ber_memrealloc(pctrls, + (npc+ndefc+1)*sizeof(LDAPControl*)); + + if (newctrls == NULL) { + rc = LDAP_NO_MEMORY; + } else { + int i; + pctrls = newctrls; + for (i=npc; i<npc+ndefc; i++) { + pctrls[i] = ldap_control_dup(defctrls[i-npc]); + if (pctrls[i] == NULL) { + rc = LDAP_NO_MEMORY; + break; + } + } + pctrls[npc+ndefc] = NULL; + } + ldap_controls_free(defctrls); /* Must be freed by library */ + } + } + } + + if ( rc == 0 ) { + if ( delete_entry ) { + rc = dodelete( dn, pctrls ); + } else if ( newrdn != NULL ) { + rc = dorename( dn, newrdn, newsup, deleteoldrdn, pctrls ); + } else { + rc = domodify( dn, pmods, pctrls, new_entry ); + } + + if ( rc == LDAP_SUCCESS ) { + rc = 0; + } + } + +leave: + if (pctrls != NULL) { + ldap_controls_free( pctrls ); + } + if ( lm != NULL ) { + ber_memfree( lm ); + } + if ( mops != NULL ) { + ber_memfree( mops ); + } + for (i=lines-1; i>=0; i--) + if ( freeval[i] ) ber_memfree( vals[i].bv_val ); + ber_memfree( btype ); + + return( rc ); +} + +/* Parse an LDIF control line of the form + control: oid [true/false] [: value] or + control: oid [true/false] [:: base64-value] or + control: oid [true/false] [:< url] + The control is added to the list of controls in *ppctrls. +*/ +static int +parse_ldif_control( + struct berval *bval, + LDAPControl ***ppctrls ) +{ + char *oid = NULL; + int criticality = 0; /* Default is false if not present */ + int i, rc=0; + char *s, *oidStart; + LDAPControl *newctrl = NULL; + LDAPControl **pctrls = NULL; + struct berval type, bv = BER_BVNULL; + int freeval = 0; + + if (ppctrls) pctrls = *ppctrls; + /* OID should come first. Validate and extract it. */ + s = bval->bv_val; + if (*s == 0) return ( LDAP_PARAM_ERROR ); + oidStart = s; + while (isdigit((unsigned char)*s) || *s == '.') { + s++; /* OID should be digits or . */ + } + if (s == oidStart) { + return ( LDAP_PARAM_ERROR ); /* OID was not present */ + } + if (*s) { /* End of OID should be space or NULL */ + if (!isspace((unsigned char)*s)) { + return ( LDAP_PARAM_ERROR ); /* else OID contained invalid chars */ + } + *s++ = 0; /* Replace space with null to terminate */ + } + + oid = ber_strdup(oidStart); + if (oid == NULL) return ( LDAP_NO_MEMORY ); + + /* Optional Criticality field is next. */ + while (*s && isspace((unsigned char)*s)) { + s++; /* Skip white space before criticality */ + } + if (strncasecmp(s, "true", 4) == 0) { + criticality = 1; + s += 4; + } + else if (strncasecmp(s, "false", 5) == 0) { + criticality = 0; + s += 5; + } + + /* Optional value field is next */ + while (*s && isspace((unsigned char)*s)) { + s++; /* Skip white space before value */ + } + if (*s) { + if (*s != ':') { /* If value is present, must start with : */ + rc = LDAP_PARAM_ERROR; + goto cleanup; + } + + /* Back up so value is in the form + a: value + a:: base64-value + a:< url + Then we can use ldif_parse_line2 to extract and decode the value + */ + s--; + *s = 'a'; + + rc = ldif_parse_line2(s, &type, &bv, &freeval); + if (rc < 0) { + rc = LDAP_PARAM_ERROR; + goto cleanup; + } + } + + /* Create a new LDAPControl structure. */ + newctrl = (LDAPControl *)ber_memalloc(sizeof(LDAPControl)); + if ( newctrl == NULL ) { + rc = LDAP_NO_MEMORY; + goto cleanup; + } + newctrl->ldctl_oid = oid; + oid = NULL; + newctrl->ldctl_iscritical = criticality; + if ( freeval ) + newctrl->ldctl_value = bv; + else + ber_dupbv( &newctrl->ldctl_value, &bv ); + + /* Add the new control to the passed-in list of controls. */ + i = 0; + if (pctrls) { + while ( pctrls[i] ) { /* Count the # of controls passed in */ + i++; + } + } + /* Allocate 1 more slot for the new control and 1 for the NULL. */ + pctrls = (LDAPControl **) ber_memrealloc(pctrls, + (i+2)*(sizeof(LDAPControl *))); + if (pctrls == NULL) { + rc = LDAP_NO_MEMORY; + goto cleanup; + } + pctrls[i] = newctrl; + newctrl = NULL; + pctrls[i+1] = NULL; + *ppctrls = pctrls; + +cleanup: + if (newctrl) { + if (newctrl->ldctl_oid) ber_memfree(newctrl->ldctl_oid); + if (newctrl->ldctl_value.bv_val) { + ber_memfree(newctrl->ldctl_value.bv_val); + } + ber_memfree(newctrl); + } + if (oid) ber_memfree(oid); + + return( rc ); +} + + +static int +domodify( + const char *dn, + LDAPMod **pmods, + LDAPControl **pctrls, + int newentry ) +{ + int rc, i, j, k, notascii, op; + struct berval *bvp; + + if ( dn == NULL ) { + fprintf( stderr, _("%s: no DN specified\n"), prog ); + return( LDAP_PARAM_ERROR ); + } + + if ( pmods == NULL ) { + /* implement "touch" (empty sequence) + * modify operation (note that there + * is no symmetry with the UNIX command, + * since \"touch\" on a non-existent entry + * will fail)*/ + printf( "warning: no attributes to %sadd (entry=\"%s\")\n", + newentry ? "" : "change or ", dn ); + + } else { + for ( i = 0; pmods[ i ] != NULL; ++i ) { + op = pmods[ i ]->mod_op & ~LDAP_MOD_BVALUES; + if( op == LDAP_MOD_ADD && ( pmods[i]->mod_bvalues == NULL )) { + fprintf( stderr, + _("%s: attribute \"%s\" has no values (entry=\"%s\")\n"), + prog, pmods[i]->mod_type, dn ); + return LDAP_PARAM_ERROR; + } + } + + if ( verbose ) { + for ( i = 0; pmods[ i ] != NULL; ++i ) { + op = pmods[ i ]->mod_op & ~LDAP_MOD_BVALUES; + printf( "%s %s:\n", + op == LDAP_MOD_REPLACE ? _("replace") : + op == LDAP_MOD_ADD ? _("add") : + op == LDAP_MOD_INCREMENT ? _("increment") : + op == LDAP_MOD_DELETE ? _("delete") : + _("unknown"), + pmods[ i ]->mod_type ); + + if ( pmods[ i ]->mod_bvalues != NULL ) { + for ( j = 0; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) { + bvp = pmods[ i ]->mod_bvalues[ j ]; + notascii = 0; + for ( k = 0; (unsigned long) k < bvp->bv_len; ++k ) { + if ( !isascii( bvp->bv_val[ k ] )) { + notascii = 1; + break; + } + } + if ( notascii ) { + printf( _("\tNOT ASCII (%ld bytes)\n"), bvp->bv_len ); + } else { + printf( "\t%s\n", bvp->bv_val ); + } + } + } + } + } + } + + if ( newentry ) { + printf( "%sadding new entry \"%s\"\n", dont ? "!" : "", dn ); + } else { + printf( "%smodifying entry \"%s\"\n", dont ? "!" : "", dn ); + } + + if ( !dont ) { + int msgid; + if ( newentry ) { + rc = ldap_add_ext( ld, dn, pmods, pctrls, NULL, &msgid ); + } else { + rc = ldap_modify_ext( ld, dn, pmods, pctrls, NULL, &msgid ); + } + + if ( rc != LDAP_SUCCESS ) { + /* print error message about failed update including DN */ + fprintf( stderr, _("%s: update failed: %s\n"), prog, dn ); + tool_perror( newentry ? "ldap_add" : "ldap_modify", + rc, NULL, NULL, NULL, NULL ); + goto done; + } + rc = process_response( ld, msgid, + newentry ? LDAP_RES_ADD : LDAP_RES_MODIFY, dn ); + + if ( verbose && rc == LDAP_SUCCESS ) { + printf( _("modify complete\n") ); + } + + } else { + rc = LDAP_SUCCESS; + } + +done: + putchar( '\n' ); + return rc; +} + + +static int +dodelete( + const char *dn, + LDAPControl **pctrls ) +{ + int rc; + int msgid; + + printf( _("%sdeleting entry \"%s\"\n"), dont ? "!" : "", dn ); + if ( !dont ) { + rc = ldap_delete_ext( ld, dn, pctrls, NULL, &msgid ); + if ( rc != LDAP_SUCCESS ) { + fprintf( stderr, _("%s: delete failed: %s\n"), prog, dn ); + tool_perror( "ldap_delete", rc, NULL, NULL, NULL, NULL ); + goto done; + } + rc = process_response( ld, msgid, LDAP_RES_DELETE, dn ); + + if ( verbose && rc == LDAP_SUCCESS ) { + printf( _("delete complete\n") ); + } + } else { + rc = LDAP_SUCCESS; + } + +done: + putchar( '\n' ); + return( rc ); +} + + +static int +dorename( + const char *dn, + const char *newrdn, + const char* newsup, + int deleteoldrdn, + LDAPControl **pctrls ) +{ + int rc; + int msgid; + + printf( _("%smodifying rdn of entry \"%s\"\n"), dont ? "!" : "", dn ); + if ( verbose ) { + printf( _("\tnew RDN: \"%s\" (%skeep existing values)\n"), + newrdn, deleteoldrdn ? _("do not ") : "" ); + } + if ( !dont ) { + rc = ldap_rename( ld, dn, newrdn, newsup, deleteoldrdn, + pctrls, NULL, &msgid ); + if ( rc != LDAP_SUCCESS ) { + fprintf( stderr, _("%s: rename failed: %s\n"), prog, dn ); + tool_perror( "ldap_rename", rc, NULL, NULL, NULL, NULL ); + goto done; + } + rc = process_response( ld, msgid, LDAP_RES_RENAME, dn ); + + if ( verbose && rc == LDAP_SUCCESS ) { + printf( _("rename complete\n") ); + } + } else { + rc = LDAP_SUCCESS; + } + +done: + putchar( '\n' ); + return( rc ); +} + +static const char * +res2str( int res ) { + switch ( res ) { + case LDAP_RES_ADD: + return "ldap_add"; + case LDAP_RES_DELETE: + return "ldap_delete"; + case LDAP_RES_MODIFY: + return "ldap_modify"; + case LDAP_RES_MODRDN: + return "ldap_rename"; + default: + assert( 0 ); + } + + return "ldap_unknown"; +} + +static int process_response( + LDAP *ld, + int msgid, + int op, + const char *dn ) +{ + LDAPMessage *res; + int rc = LDAP_OTHER, msgtype; + struct timeval tv = { 0, 0 }; + int err; + char *text = NULL, *matched = NULL, **refs = NULL; + LDAPControl **ctrls = NULL; + + for ( ; ; ) { + tv.tv_sec = 0; + tv.tv_usec = 100000; + + rc = ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res ); + if ( tool_check_abandon( ld, msgid ) ) { + return LDAP_CANCELLED; + } + + if ( rc == -1 ) { + ldap_get_option( ld, LDAP_OPT_RESULT_CODE, &rc ); + tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL ); + return rc; + } + + if ( rc != 0 ) { + break; + } + } + + msgtype = ldap_msgtype( res ); + + rc = ldap_parse_result( ld, res, &err, &matched, &text, &refs, &ctrls, 1 ); + if ( rc == LDAP_SUCCESS ) rc = err; + +#ifdef LDAP_X_TXN + if ( rc == LDAP_X_TXN_SPECIFY_OKAY ) { + rc = LDAP_SUCCESS; + } else +#endif + if ( rc != LDAP_SUCCESS ) { + tool_perror( res2str( op ), rc, NULL, matched, text, refs ); + } else if ( msgtype != op ) { + fprintf( stderr, "%s: msgtype: expected %d got %d\n", + res2str( op ), op, msgtype ); + rc = LDAP_OTHER; + } + + if ( text ) ldap_memfree( text ); + if ( matched ) ldap_memfree( matched ); + if ( refs ) ber_memvfree( (void **)refs ); + + if ( ctrls ) { + tool_print_ctrls( ld, ctrls ); + ldap_controls_free( ctrls ); + } + + return rc; +} diff --git a/clients/tools/ldapmodrdn.c b/clients/tools/ldapmodrdn.c new file mode 100644 index 0000000..a726599 --- /dev/null +++ b/clients/tools/ldapmodrdn.c @@ -0,0 +1,330 @@ +/* ldapmodrdn.c - generic program to modify an entry's RDN using LDAP */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * Portions Copyright 1998-2003 Kurt D. Zeilenga. + * Portions Copyright 1998-2001 Net Boolean Incorporated. + * Portions Copyright 2001-2003 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 1999, Juan C. Gomez, All rights reserved. + * This software is not subject to any license of Silicon Graphics + * Inc. or Purdue University. + * + * Redistribution and use in source and binary forms are permitted + * without restriction or fee of any kind as long as this notice + * is preserved. + */ +/* Portions Copyright (c) 1992-1996 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. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). Additional significant contributors + * include: + * Kurt D. Zeilenga + * Juan C Gomez + */ + + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/ctype.h> +#include <ac/string.h> +#include <ac/unistd.h> +#include <ac/socket.h> +#include <ac/time.h> + +#include <ldap.h> +#include "lutil.h" +#include "lutil_ldap.h" +#include "ldap_defaults.h" + +#include "common.h" + + +static char *newSuperior = NULL; +static int remove_old_RDN = 0; + + +static int domodrdn( + LDAP *ld, + char *dn, + char *rdn, + char *newSuperior, + int remove ); /* flag: remove old RDN */ + +void +usage( void ) +{ + fprintf( stderr, _("Rename LDAP entries\n\n")); + fprintf( stderr, _("usage: %s [options] [dn rdn]\n"), prog); + fprintf( stderr, _(" dn rdn: If given, rdn will replace the RDN of the entry specified by DN\n")); + fprintf( stderr, _(" If not given, the list of modifications is read from stdin or\n")); + fprintf( stderr, _(" from the file specified by \"-f file\" (see man page).\n")); + fprintf( stderr, _("Rename options:\n")); + fprintf( stderr, _(" -c continuous operation mode (do not stop on errors)\n")); + fprintf( stderr, _(" -f file read operations from `file'\n")); + fprintf( stderr, _(" -M enable Manage DSA IT control (-MM to make critical)\n")); + fprintf( stderr, _(" -P version protocol version (default: 3)\n")); + fprintf( stderr, _(" -r remove old RDN\n")); + fprintf( stderr, _(" -s newsup new superior entry\n")); + tool_common_usage(); + exit( EXIT_FAILURE ); +} + + +const char options[] = "rs:" + "cd:D:e:f:h:H:IMnNO:o:p:P:QR:U:vVw:WxX:y:Y:Z"; + +int +handle_private_option( int i ) +{ + switch ( i ) { +#if 0 + int crit; + char *control, *cvalue; + case 'E': /* modrdn extensions */ + if( protocol == LDAP_VERSION2 ) { + fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"), + prog, version ); + exit( EXIT_FAILURE ); + } + + /* should be extended to support comma separated list of + * [!]key[=value] parameters, e.g. -E !foo,bar=567 + */ + + crit = 0; + cvalue = NULL; + if( optarg[0] == '!' ) { + crit = 1; + optarg++; + } + + control = strdup( optarg ); + if ( (cvalue = strchr( control, '=' )) != NULL ) { + *cvalue++ = '\0'; + } + fprintf( stderr, _("Invalid modrdn extension name: %s\n"), control ); + usage(); +#endif + + case 'r': /* remove old RDN */ + remove_old_RDN++; + break; + + case 's': /* newSuperior */ + if( protocol == LDAP_VERSION2 ) { + fprintf( stderr, _("%s: -X incompatible with LDAPv%d\n"), + prog, protocol ); + exit( EXIT_FAILURE ); + } + newSuperior = strdup( optarg ); + protocol = LDAP_VERSION3; + break; + + default: + return 0; + } + return 1; +} + + +int +main(int argc, char **argv) +{ + char *entrydn = NULL, *rdn = NULL, buf[ 4096 ]; + FILE *fp = NULL; + LDAP *ld = NULL; + int rc, retval, havedn; + + tool_init( TOOL_MODRDN ); + prog = lutil_progname( "ldapmodrdn", argc, argv ); + + tool_args( argc, argv ); + + havedn = 0; + if (argc - optind == 2) { + if (( rdn = strdup( argv[argc - 1] )) == NULL ) { + perror( "strdup" ); + retval = EXIT_FAILURE; + goto fail; + } + if (( entrydn = strdup( argv[argc - 2] )) == NULL ) { + perror( "strdup" ); + retval = EXIT_FAILURE; + goto fail; + } + ++havedn; + } else if ( argc - optind != 0 ) { + fprintf( stderr, _("%s: invalid number of arguments (%d), only two allowed\n"), prog, argc-optind ); + usage(); + } + + if ( infile != NULL ) { + if (( fp = fopen( infile, "r" )) == NULL ) { + perror( infile ); + retval = EXIT_FAILURE; + goto fail; + } + } else { + fp = stdin; + } + + ld = tool_conn_setup( 0, 0 ); + + tool_bind( ld ); + + tool_server_controls( ld, NULL, 0 ); + + retval = rc = 0; + if (havedn) + retval = domodrdn( ld, entrydn, rdn, newSuperior, remove_old_RDN ); + else while ((rc == 0 || contoper) && fgets(buf, sizeof(buf), fp) != NULL) { + if ( *buf != '\n' ) { /* blank lines optional, skip */ + buf[ strlen( buf ) - 1 ] = '\0'; /* remove nl */ + + if ( havedn ) { /* have DN, get RDN */ + if (( rdn = strdup( buf )) == NULL ) { + perror( "strdup" ); + retval = EXIT_FAILURE; + goto fail; + } + rc = domodrdn(ld, entrydn, rdn, newSuperior, remove_old_RDN ); + if ( rc != 0 ) + retval = rc; + havedn = 0; + free( rdn ); rdn = NULL; + free( entrydn ); entrydn = NULL; + } else if ( !havedn ) { /* don't have DN yet */ + if (( entrydn = strdup( buf )) == NULL ) { + retval = EXIT_FAILURE; + goto fail; + } + ++havedn; + } + } + } + +fail: + if ( fp && fp != stdin ) fclose( fp ); + if ( entrydn ) free( entrydn ); + if ( rdn ) free( rdn ); + tool_exit( ld, retval ); +} + +static int domodrdn( + LDAP *ld, + char *dn, + char *rdn, + char *newSuperior, + int remove ) /* flag: remove old RDN */ +{ + int rc, code, id; + char *matcheddn=NULL, *text=NULL, **refs=NULL; + LDAPControl **ctrls = NULL; + LDAPMessage *res; + + if ( verbose ) { + printf( _("Renaming \"%s\"\n"), dn ); + printf( _("\tnew rdn=\"%s\" (%s old rdn)\n"), + rdn, remove ? _("delete") : _("keep") ); + if( newSuperior != NULL ) { + printf(_("\tnew parent=\"%s\"\n"), newSuperior); + } + } + + if( dont ) return LDAP_SUCCESS; + + rc = ldap_rename( ld, dn, rdn, newSuperior, remove, + NULL, NULL, &id ); + + if ( rc != LDAP_SUCCESS ) { + fprintf( stderr, "%s: ldap_rename: %s (%d)\n", + prog, ldap_err2string( rc ), rc ); + return rc; + } + + for ( ; ; ) { + struct timeval tv = { 0, 0 }; + + if ( tool_check_abandon( ld, id ) ) { + return LDAP_CANCELLED; + } + + tv.tv_sec = 0; + tv.tv_usec = 100000; + + rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res ); + if ( rc < 0 ) { + tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL ); + return rc; + } + + if ( rc != 0 ) { + break; + } + } + + rc = ldap_parse_result( ld, res, &code, &matcheddn, &text, &refs, &ctrls, 1 ); + + if( rc != LDAP_SUCCESS ) { + fprintf( stderr, "%s: ldap_parse_result: %s (%d)\n", + prog, ldap_err2string( rc ), rc ); + return rc; + } + + if( verbose || code != LDAP_SUCCESS || + (matcheddn && *matcheddn) || (text && *text) || (refs && *refs) ) + { + printf( _("Rename Result: %s (%d)\n"), + ldap_err2string( code ), code ); + + if( text && *text ) { + printf( _("Additional info: %s\n"), text ); + } + + if( matcheddn && *matcheddn ) { + printf( _("Matched DN: %s\n"), matcheddn ); + } + + if( refs ) { + int i; + for( i=0; refs[i]; i++ ) { + printf(_("Referral: %s\n"), refs[i] ); + } + } + } + + if (ctrls) { + tool_print_ctrls( ld, ctrls ); + ldap_controls_free( ctrls ); + } + + ber_memfree( text ); + ber_memfree( matcheddn ); + ber_memvfree( (void **) refs ); + + return code; +} diff --git a/clients/tools/ldappasswd.c b/clients/tools/ldappasswd.c new file mode 100644 index 0000000..fd763bb --- /dev/null +++ b/clients/tools/ldappasswd.c @@ -0,0 +1,413 @@ +/* ldappasswd -- a tool for change LDAP passwords */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * Portions Copyright 1998-2003 Kurt D. Zeilenga. + * Portions Copyright 1998-2001 Net Boolean Incorporated. + * Portions Copyright 2001-2003 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) 1992-1996 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. + */ +/* ACKNOWLEDGEMENTS: + * The original ldappasswd(1) tool was developed by Dave Storey (F5 + * Network), based on other OpenLDAP client tools (which are, of + * course, based on U-MICH LDAP). This version was rewritten + * by Kurt D. Zeilenga (based on other OpenLDAP client tools). + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/ctype.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/unistd.h> + +#include <ldap.h> +#include "lutil.h" +#include "lutil_ldap.h" +#include "ldap_defaults.h" + +#include "common.h" + + +static struct berval newpw = { 0, NULL }; +static struct berval oldpw = { 0, NULL }; + +static int want_newpw = 0; +static int want_oldpw = 0; + +static char *oldpwfile = NULL; +static char *newpwfile = NULL; + +void +usage( void ) +{ + fprintf( stderr, _("Change password of an LDAP user\n\n")); + fprintf( stderr,_("usage: %s [options] [user]\n"), prog); + fprintf( stderr, _(" user: the authentication identity, commonly a DN\n")); + fprintf( stderr, _("Password change options:\n")); + fprintf( stderr, _(" -a secret old password\n")); + fprintf( stderr, _(" -A prompt for old password\n")); + fprintf( stderr, _(" -t file read file for old password\n")); + fprintf( stderr, _(" -s secret new password\n")); + fprintf( stderr, _(" -S prompt for new password\n")); + fprintf( stderr, _(" -T file read file for new password\n")); + tool_common_usage(); + exit( EXIT_FAILURE ); +} + + +const char options[] = "a:As:St:T:" + "d:D:e:h:H:InNO:o:p:QR:U:vVw:WxX:y:Y:Z"; + +int +handle_private_option( int i ) +{ + switch ( i ) { +#if 0 + case 'E': /* passwd extensions */ { + int crit; + char *control, *cvalue; + if( protocol == LDAP_VERSION2 ) { + fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"), + prog, protocol ); + exit( EXIT_FAILURE ); + } + + /* should be extended to support comma separated list of + * [!]key[=value] parameters, e.g. -E !foo,bar=567 + */ + + crit = 0; + cvalue = NULL; + if( optarg[0] == '!' ) { + crit = 1; + optarg++; + } + + control = strdup( optarg ); + if ( (cvalue = strchr( control, '=' )) != NULL ) { + *cvalue++ = '\0'; + } + fprintf( stderr, _("Invalid passwd extension name: %s\n"), control ); + usage(); + } +#endif + + case 'a': /* old password (secret) */ + oldpw.bv_val = strdup( optarg ); + { + char* p; + for( p = optarg; *p != '\0'; p++ ) { + *p = '\0'; + } + } + oldpw.bv_len = strlen( oldpw.bv_val ); + break; + + case 'A': /* prompt for old password */ + want_oldpw++; + break; + + case 's': /* new password (secret) */ + newpw.bv_val = strdup (optarg); + { + char* p; + for( p = optarg; *p != '\0'; p++ ) { + *p = '\0'; + } + } + newpw.bv_len = strlen( newpw.bv_val ); + break; + + case 'S': /* prompt for user password */ + want_newpw++; + break; + + case 't': + oldpwfile = optarg; + break; + + case 'T': + newpwfile = optarg; + break; + + default: + return 0; + } + return 1; +} + + +int +main( int argc, char *argv[] ) +{ + int rc; + char *user = NULL; + + LDAP *ld = NULL; + struct berval bv = {0, NULL}; + BerElement *ber = NULL; + + int id, code = LDAP_OTHER; + LDAPMessage *res; + char *matcheddn = NULL, *text = NULL, **refs = NULL; + char *retoid = NULL; + struct berval *retdata = NULL; + LDAPControl **ctrls = NULL; + + tool_init( TOOL_PASSWD ); + prog = lutil_progname( "ldappasswd", argc, argv ); + + /* LDAPv3 only */ + protocol = LDAP_VERSION3; + + tool_args( argc, argv ); + + if( argc - optind > 1 ) { + usage(); + } else if ( argc - optind == 1 ) { + user = strdup( argv[optind] ); + } else { + user = NULL; + } + + if( oldpwfile ) { + rc = lutil_get_filed_password( oldpwfile, &oldpw ); + if( rc ) { + rc = EXIT_FAILURE; + goto done; + } + } + + if( want_oldpw && oldpw.bv_val == NULL ) { + /* prompt for old password */ + char *ckoldpw; + oldpw.bv_val = strdup(getpassphrase(_("Old password: "))); + ckoldpw = getpassphrase(_("Re-enter old password: ")); + + if( oldpw.bv_val == NULL || ckoldpw == NULL || + strcmp( oldpw.bv_val, ckoldpw )) + { + fprintf( stderr, _("passwords do not match\n") ); + rc = EXIT_FAILURE; + goto done; + } + + oldpw.bv_len = strlen( oldpw.bv_val ); + } + + if( newpwfile ) { + rc = lutil_get_filed_password( newpwfile, &newpw ); + if( rc ) { + rc = EXIT_FAILURE; + goto done; + } + } + + if( want_newpw && newpw.bv_val == NULL ) { + /* prompt for new password */ + char *cknewpw; + newpw.bv_val = strdup(getpassphrase(_("New password: "))); + cknewpw = getpassphrase(_("Re-enter new password: ")); + + if( newpw.bv_val == NULL || cknewpw == NULL || + strcmp( newpw.bv_val, cknewpw )) + { + fprintf( stderr, _("passwords do not match\n") ); + rc = EXIT_FAILURE; + goto done; + } + + newpw.bv_len = strlen( newpw.bv_val ); + } + + ld = tool_conn_setup( 0, 0 ); + + tool_bind( ld ); + + if( user != NULL || oldpw.bv_val != NULL || newpw.bv_val != NULL ) { + /* build the password modify request data */ + ber = ber_alloc_t( LBER_USE_DER ); + + if( ber == NULL ) { + perror( "ber_alloc_t" ); + rc = EXIT_FAILURE; + goto done; + } + + ber_printf( ber, "{" /*}*/ ); + + if( user != NULL ) { + ber_printf( ber, "ts", + LDAP_TAG_EXOP_MODIFY_PASSWD_ID, user ); + free(user); + } + + if( oldpw.bv_val != NULL ) { + ber_printf( ber, "tO", + LDAP_TAG_EXOP_MODIFY_PASSWD_OLD, &oldpw ); + free(oldpw.bv_val); + } + + if( newpw.bv_val != NULL ) { + ber_printf( ber, "tO", + LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, &newpw ); + free(newpw.bv_val); + } + + ber_printf( ber, /*{*/ "N}" ); + + rc = ber_flatten2( ber, &bv, 0 ); + + if( rc < 0 ) { + perror( "ber_flatten2" ); + rc = EXIT_FAILURE; + goto done; + } + } + + if ( dont ) { + rc = LDAP_SUCCESS; + goto done; + } + + tool_server_controls( ld, NULL, 0); + + rc = ldap_extended_operation( ld, + LDAP_EXOP_MODIFY_PASSWD, bv.bv_val ? &bv : NULL, + NULL, NULL, &id ); + + ber_free( ber, 1 ); + + if( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_extended_operation", rc, NULL, NULL, NULL, NULL ); + rc = EXIT_FAILURE; + goto done; + } + + for ( ; ; ) { + struct timeval tv; + + if ( tool_check_abandon( ld, id ) ) { + tool_exit( ld, LDAP_CANCELLED ); + } + + tv.tv_sec = 0; + tv.tv_usec = 100000; + + rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res ); + if ( rc < 0 ) { + tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL ); + tool_exit( ld, rc ); + } + + if ( rc != 0 ) { + break; + } + } + + rc = ldap_parse_result( ld, res, + &code, &matcheddn, &text, &refs, &ctrls, 0 ); + if( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_parse_result", rc, NULL, NULL, NULL, NULL ); + rc = EXIT_FAILURE; + goto done; + } + + rc = ldap_parse_extended_result( ld, res, &retoid, &retdata, 1 ); + if( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_parse_extended_result", rc, NULL, NULL, NULL, NULL ); + rc = EXIT_FAILURE; + goto done; + } + + if( retdata != NULL ) { + ber_tag_t tag; + char *s; + ber = ber_init( retdata ); + + if( ber == NULL ) { + perror( "ber_init" ); + rc = EXIT_FAILURE; + goto done; + } + + /* we should check the tag */ + tag = ber_scanf( ber, "{a}", &s); + + if( tag == LBER_ERROR ) { + perror( "ber_scanf" ); + } else { + printf(_("New password: %s\n"), s); + ber_memfree( s ); + } + + ber_free( ber, 1 ); + + } else if ( code == LDAP_SUCCESS && newpw.bv_val == NULL ) { + tool_perror( "ldap_parse_extended_result", LDAP_DECODING_ERROR, + " new password expected", NULL, NULL, NULL ); + } + + if( verbose || code != LDAP_SUCCESS || + ( matcheddn && *matcheddn ) || ( text && *text ) || refs || ctrls ) + { + printf( _("Result: %s (%d)\n"), ldap_err2string( code ), code ); + + if( text && *text ) { + printf( _("Additional info: %s\n"), text ); + } + + if( matcheddn && *matcheddn ) { + printf( _("Matched DN: %s\n"), matcheddn ); + } + + if( refs ) { + int i; + for( i=0; refs[i]; i++ ) { + printf(_("Referral: %s\n"), refs[i] ); + } + } + + if( ctrls ) { + tool_print_ctrls( ld, ctrls ); + ldap_controls_free( ctrls ); + } + } + + ber_memfree( text ); + ber_memfree( matcheddn ); + ber_memvfree( (void **) refs ); + ber_memfree( retoid ); + ber_bvfree( retdata ); + + rc = ( code == LDAP_SUCCESS ) ? EXIT_SUCCESS : EXIT_FAILURE; + +done: + /* disconnect from server */ + tool_exit( ld, rc ); +} diff --git a/clients/tools/ldapsearch.c b/clients/tools/ldapsearch.c new file mode 100644 index 0000000..234b1a0 --- /dev/null +++ b/clients/tools/ldapsearch.c @@ -0,0 +1,1926 @@ +/* ldapsearch -- a tool for searching LDAP directories */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * Portions Copyright 1998-2003 Kurt D. Zeilenga. + * Portions Copyright 1998-2001 Net Boolean Incorporated. + * Portions Copyright 2001-2003 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) 1992-1996 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. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). Additional significant contributors + * include: + * Jong Hyuk Choi + * Lynn Moss + * Mikhail Sahalaev + * Kurt D. Zeilenga + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/ctype.h> +#include <ac/string.h> +#include <ac/unistd.h> +#include <ac/errno.h> +#include <ac/time.h> + +#include <sys/stat.h> + +#include <ac/signal.h> + +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_IO_H +#include <io.h> +#endif + +#include <ldap.h> + +#include "ldif.h" +#include "lutil.h" +#include "lutil_ldap.h" +#include "ldap_defaults.h" +#include "ldap_pvt.h" + +#include "common.h" + +#if !LDAP_DEPRECATED +/* + * NOTE: we use this deprecated function only because + * we want ldapsearch to provide some client-side sorting + * capability. + */ +/* from ldap.h */ +typedef int (LDAP_SORT_AD_CMP_PROC) LDAP_P(( /* deprecated */ + LDAP_CONST char *left, + LDAP_CONST char *right )); + +LDAP_F( int ) /* deprecated */ +ldap_sort_entries LDAP_P(( LDAP *ld, + LDAPMessage **chain, + LDAP_CONST char *attr, + LDAP_SORT_AD_CMP_PROC *cmp )); +#endif + +static int scope = LDAP_SCOPE_SUBTREE; +static int deref = -1; +static int attrsonly; +static int timelimit = -1; +static int sizelimit = -1; + +static char *control; + +static char *def_tmpdir; +static char *def_urlpre; + +#if defined(__CYGWIN__) || defined(__MINGW32__) +/* Turn off commandline globbing, otherwise you cannot search for + * attribute '*' + */ +int _CRT_glob = 0; +#endif + +void +usage( void ) +{ + fprintf( stderr, _("usage: %s [options] [filter [attributes...]]\nwhere:\n"), prog); + fprintf( stderr, _(" filter\tRFC 4515 compliant LDAP search filter\n")); + fprintf( stderr, _(" attributes\twhitespace-separated list of attribute descriptions\n")); + fprintf( stderr, _(" which may include:\n")); + fprintf( stderr, _(" 1.1 no attributes\n")); + fprintf( stderr, _(" * all user attributes\n")); + fprintf( stderr, _(" + all operational attributes\n")); + + + fprintf( stderr, _("Search options:\n")); + fprintf( stderr, _(" -a deref one of never (default), always, search, or find\n")); + fprintf( stderr, _(" -A retrieve attribute names only (no values)\n")); + fprintf( stderr, _(" -b basedn base dn for search\n")); + fprintf( stderr, _(" -c continuous operation mode (do not stop on errors)\n")); + fprintf( stderr, _(" -E [!]<ext>[=<extparam>] search extensions (! indicates criticality)\n")); + fprintf( stderr, _(" [!]domainScope (domain scope)\n")); + fprintf( stderr, _(" !dontUseCopy (Don't Use Copy)\n")); + fprintf( stderr, _(" [!]mv=<filter> (RFC 3876 matched values filter)\n")); + fprintf( stderr, _(" [!]pr=<size>[/prompt|noprompt] (RFC 2696 paged results/prompt)\n")); + fprintf( stderr, _(" [!]sss=[-]<attr[:OID]>[/[-]<attr[:OID]>...]\n")); + fprintf( stderr, _(" (RFC 2891 server side sorting)\n")); + fprintf( stderr, _(" [!]subentries[=true|false] (RFC 3672 subentries)\n")); + fprintf( stderr, _(" [!]sync=ro[/<cookie>] (RFC 4533 LDAP Sync refreshOnly)\n")); + fprintf( stderr, _(" rp[/<cookie>][/<slimit>] (refreshAndPersist)\n")); + fprintf( stderr, _(" [!]vlv=<before>/<after>(/<offset>/<count>|:<value>)\n")); + fprintf( stderr, _(" (ldapv3-vlv-09 virtual list views)\n")); +#ifdef LDAP_CONTROL_X_DEREF + fprintf( stderr, _(" [!]deref=derefAttr:attr[,...][;derefAttr:attr[,...][;...]]\n")); +#endif + fprintf( stderr, _(" [!]<oid>[=:<b64value>] (generic control; no response handling)\n")); + fprintf( stderr, _(" -f file read operations from `file'\n")); + fprintf( stderr, _(" -F prefix URL prefix for files (default: %s)\n"), def_urlpre); + fprintf( stderr, _(" -l limit time limit (in seconds, or \"none\" or \"max\") for search\n")); + fprintf( stderr, _(" -L print responses in LDIFv1 format\n")); + fprintf( stderr, _(" -LL print responses in LDIF format without comments\n")); + fprintf( stderr, _(" -LLL print responses in LDIF format without comments\n")); + fprintf( stderr, _(" and version\n")); + fprintf( stderr, _(" -M enable Manage DSA IT control (-MM to make critical)\n")); + fprintf( stderr, _(" -P version protocol version (default: 3)\n")); + fprintf( stderr, _(" -s scope one of base, one, sub or children (search scope)\n")); + fprintf( stderr, _(" -S attr sort the results by attribute `attr'\n")); + fprintf( stderr, _(" -t write binary values to files in temporary directory\n")); + fprintf( stderr, _(" -tt write all values to files in temporary directory\n")); + fprintf( stderr, _(" -T path write files to directory specified by path (default: %s)\n"), def_tmpdir); + fprintf( stderr, _(" -u include User Friendly entry names in the output\n")); + fprintf( stderr, _(" -z limit size limit (in entries, or \"none\" or \"max\") for search\n")); + tool_common_usage(); + exit( EXIT_FAILURE ); +} + +static void print_entry LDAP_P(( + LDAP *ld, + LDAPMessage *entry, + int attrsonly)); + +static void print_reference( + LDAP *ld, + LDAPMessage *reference ); + +static void print_extended( + LDAP *ld, + LDAPMessage *extended ); + +static void print_partial( + LDAP *ld, + LDAPMessage *partial ); + +static int print_result( + LDAP *ld, + LDAPMessage *result, + int search ); + +static int dosearch LDAP_P(( + LDAP *ld, + char *base, + int scope, + char *filtpatt, + char *value, + char **attrs, + int attrsonly, + LDAPControl **sctrls, + LDAPControl **cctrls, + struct timeval *timeout, + int sizelimit )); + +static char *tmpdir = NULL; +static char *urlpre = NULL; +static char *base = NULL; +static char *sortattr = NULL; +static int includeufn, vals2tmp = 0; + +static int subentries = 0, valuesReturnFilter = 0; +static char *vrFilter = NULL; + +#ifdef LDAP_CONTROL_DONTUSECOPY +static int dontUseCopy = 0; +#endif + +static int domainScope = 0; + +static int sss = 0; +static LDAPSortKey **sss_keys = NULL; + +static int vlv = 0; +static LDAPVLVInfo vlvInfo; +static struct berval vlvValue; + +static int ldapsync = 0; +static struct berval sync_cookie = { 0, NULL }; +static int sync_slimit = -1; + +/* cookie and morePagedResults moved to common.c */ +static int pagedResults = 0; +static int pagePrompt = 1; +static ber_int_t pageSize = 0; +static ber_int_t entriesLeft = 0; +static int npagedresponses; +static int npagedentries; +static int npagedreferences; +static int npagedextended; +static int npagedpartial; + +static LDAPControl *c = NULL; +static int nctrls = 0; +static int save_nctrls = 0; + +#ifdef LDAP_CONTROL_X_DEREF +static int derefcrit; +static LDAPDerefSpec *ds; +static struct berval derefval; +#endif + +static int +ctrl_add( void ) +{ + LDAPControl *tmpc; + + nctrls++; + tmpc = realloc( c, sizeof( LDAPControl ) * nctrls ); + if ( tmpc == NULL ) { + nctrls--; + fprintf( stderr, + _("unable to make room for control; out of memory?\n")); + return -1; + } + c = tmpc; + + return 0; +} + +static void +urlize(char *url) +{ + char *p; + + if (*LDAP_DIRSEP != '/') { + for (p = url; *p; p++) { + if (*p == *LDAP_DIRSEP) + *p = '/'; + } + } +} + +static int +parse_vlv(char *cvalue) +{ + char *keyp, *key2; + int num1, num2; + + keyp = cvalue; + if ( sscanf( keyp, "%d/%d", &num1, &num2 ) != 2 ) { + fprintf( stderr, + _("VLV control value \"%s\" invalid\n"), + cvalue ); + return -1; + } + vlvInfo.ldvlv_before_count = num1; + vlvInfo.ldvlv_after_count = num2; + keyp = strchr( keyp, '/' ) + 1; + key2 = strchr( keyp, '/' ); + if ( key2 ) { + keyp = key2 + 1; + if ( sscanf( keyp, "%d/%d", &num1, &num2 ) != 2 ) { + fprintf( stderr, + _("VLV control value \"%s\" invalid\n"), + cvalue ); + return -1; + } + vlvInfo.ldvlv_offset = num1; + vlvInfo.ldvlv_count = num2; + vlvInfo.ldvlv_attrvalue = NULL; + } else { + key2 = strchr( keyp, ':' ); + if ( !key2 ) { + fprintf( stderr, + _("VLV control value \"%s\" invalid\n"), + cvalue ); + return -1; + } + ber_str2bv( key2+1, 0, 0, &vlvValue ); + vlvInfo.ldvlv_attrvalue = &vlvValue; + } + return 0; +} + +const char options[] = "a:Ab:cE:F:l:Ls:S:tT:uz:" + "Cd:D:e:f:h:H:IMnNO:o:p:P:QR:U:vVw:WxX:y:Y:Z"; + +int +handle_private_option( int i ) +{ + int crit, ival; + char *cvalue, *next; + switch ( i ) { + case 'a': /* set alias deref option */ + if ( strcasecmp( optarg, "never" ) == 0 ) { + deref = LDAP_DEREF_NEVER; + } else if ( strncasecmp( optarg, "search", sizeof("search")-1 ) == 0 ) { + deref = LDAP_DEREF_SEARCHING; + } else if ( strncasecmp( optarg, "find", sizeof("find")-1 ) == 0 ) { + deref = LDAP_DEREF_FINDING; + } else if ( strcasecmp( optarg, "always" ) == 0 ) { + deref = LDAP_DEREF_ALWAYS; + } else { + fprintf( stderr, + _("alias deref should be never, search, find, or always\n") ); + usage(); + } + break; + case 'A': /* retrieve attribute names only -- no values */ + ++attrsonly; + break; + case 'b': /* search base */ + base = ber_strdup( optarg ); + break; + case 'E': /* search extensions */ + if( protocol == LDAP_VERSION2 ) { + fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"), + prog, protocol ); + exit( EXIT_FAILURE ); + } + + /* should be extended to support comma separated list of + * [!]key[=value] parameters, e.g. -E !foo,bar=567 + */ + + crit = 0; + cvalue = NULL; + if( optarg[0] == '!' ) { + crit = 1; + optarg++; + } + + control = ber_strdup( optarg ); + if ( (cvalue = strchr( control, '=' )) != NULL ) { + *cvalue++ = '\0'; + } + + if ( strcasecmp( control, "mv" ) == 0 ) { + /* ValuesReturnFilter control */ + if( valuesReturnFilter ) { + fprintf( stderr, + _("ValuesReturnFilter previously specified\n")); + exit( EXIT_FAILURE ); + } + valuesReturnFilter= 1 + crit; + + if ( cvalue == NULL ) { + fprintf( stderr, + _("missing filter in ValuesReturnFilter control\n")); + exit( EXIT_FAILURE ); + } + + vrFilter = cvalue; + protocol = LDAP_VERSION3; + + } else if ( strcasecmp( control, "pr" ) == 0 ) { + int num, tmp; + /* PagedResults control */ + if ( pagedResults != 0 ) { + fprintf( stderr, + _("PagedResultsControl previously specified\n") ); + exit( EXIT_FAILURE ); + } + if ( vlv != 0 ) { + fprintf( stderr, + _("PagedResultsControl incompatible with VLV\n") ); + exit( EXIT_FAILURE ); + } + + if( cvalue != NULL ) { + char *promptp; + + promptp = strchr( cvalue, '/' ); + if ( promptp != NULL ) { + *promptp++ = '\0'; + if ( strcasecmp( promptp, "prompt" ) == 0 ) { + pagePrompt = 1; + } else if ( strcasecmp( promptp, "noprompt" ) == 0) { + pagePrompt = 0; + } else { + fprintf( stderr, + _("Invalid value for PagedResultsControl," + " %s/%s.\n"), cvalue, promptp ); + exit( EXIT_FAILURE ); + } + } + num = sscanf( cvalue, "%d", &tmp ); + if ( num != 1 ) { + fprintf( stderr, + _("Invalid value for PagedResultsControl, %s.\n"), + cvalue ); + exit( EXIT_FAILURE ); + } + } else { + fprintf(stderr, _("Invalid value for PagedResultsControl.\n")); + exit( EXIT_FAILURE ); + } + pageSize = (ber_int_t) tmp; + pagedResults = 1 + crit; + +#ifdef LDAP_CONTROL_DONTUSECOPY + } else if ( strcasecmp( control, "dontUseCopy" ) == 0 ) { + if( dontUseCopy ) { + fprintf( stderr, + _("dontUseCopy control previously specified\n")); + exit( EXIT_FAILURE ); + } + if( cvalue != NULL ) { + fprintf( stderr, + _("dontUseCopy: no control value expected\n") ); + usage(); + } + if( !crit ) { + fprintf( stderr, + _("dontUseCopy: critical flag required\n") ); + usage(); + } + + dontUseCopy = 1 + crit; +#endif + } else if ( strcasecmp( control, "domainScope" ) == 0 ) { + if( domainScope ) { + fprintf( stderr, + _("domainScope control previously specified\n")); + exit( EXIT_FAILURE ); + } + if( cvalue != NULL ) { + fprintf( stderr, + _("domainScope: no control value expected\n") ); + usage(); + } + + domainScope = 1 + crit; + + } else if ( strcasecmp( control, "sss" ) == 0 ) { + char *keyp; + if( sss ) { + fprintf( stderr, + _("server side sorting control previously specified\n")); + exit( EXIT_FAILURE ); + } + if( cvalue == NULL ) { + fprintf( stderr, + _("missing specification of sss control\n") ); + exit( EXIT_FAILURE ); + } + keyp = cvalue; + while ( ( keyp = strchr(keyp, '/') ) != NULL ) { + *keyp++ = ' '; + } + if ( ldap_create_sort_keylist( &sss_keys, cvalue )) { + fprintf( stderr, + _("server side sorting control value \"%s\" invalid\n"), + cvalue ); + exit( EXIT_FAILURE ); + } + + sss = 1 + crit; + + } else if ( strcasecmp( control, "subentries" ) == 0 ) { + if( subentries ) { + fprintf( stderr, + _("subentries control previously specified\n")); + exit( EXIT_FAILURE ); + } + if( cvalue == NULL || strcasecmp( cvalue, "true") == 0 ) { + subentries = 2; + } else if ( strcasecmp( cvalue, "false") == 0 ) { + subentries = 1; + } else { + fprintf( stderr, + _("subentries control value \"%s\" invalid\n"), + cvalue ); + exit( EXIT_FAILURE ); + } + if( crit ) subentries *= -1; + + } else if ( strcasecmp( control, "sync" ) == 0 ) { + char *cookiep; + char *slimitp; + if ( ldapsync ) { + fprintf( stderr, _("sync control previously specified\n") ); + exit( EXIT_FAILURE ); + } + if ( cvalue == NULL ) { + fprintf( stderr, _("missing specification of sync control\n")); + exit( EXIT_FAILURE ); + } + if ( strncasecmp( cvalue, "ro", 2 ) == 0 ) { + ldapsync = LDAP_SYNC_REFRESH_ONLY; + cookiep = strchr( cvalue, '/' ); + if ( cookiep != NULL ) { + cookiep++; + if ( *cookiep != '\0' ) { + ber_str2bv( cookiep, 0, 0, &sync_cookie ); + } + } + } else if ( strncasecmp( cvalue, "rp", 2 ) == 0 ) { + ldapsync = LDAP_SYNC_REFRESH_AND_PERSIST; + cookiep = strchr( cvalue, '/' ); + if ( cookiep != NULL ) { + *cookiep++ = '\0'; + cvalue = cookiep; + } + slimitp = strchr( cvalue, '/' ); + if ( slimitp != NULL ) { + *slimitp++ = '\0'; + } + if ( cookiep != NULL && *cookiep != '\0' ) + ber_str2bv( cookiep, 0, 0, &sync_cookie ); + if ( slimitp != NULL && *slimitp != '\0' ) { + ival = strtol( slimitp, &next, 10 ); + if ( next == NULL || next[0] != '\0' ) { + fprintf( stderr, _("Unable to parse sync control value \"%s\"\n"), slimitp ); + exit( EXIT_FAILURE ); + } + sync_slimit = ival; + } + } else { + fprintf( stderr, _("sync control value \"%s\" invalid\n"), + cvalue ); + exit( EXIT_FAILURE ); + } + if ( crit ) ldapsync *= -1; + + } else if ( strcasecmp( control, "vlv" ) == 0 ) { + if( vlv ) { + fprintf( stderr, + _("virtual list view control previously specified\n")); + exit( EXIT_FAILURE ); + } + if ( pagedResults != 0 ) { + fprintf( stderr, + _("PagedResultsControl incompatible with VLV\n") ); + exit( EXIT_FAILURE ); + } + if( cvalue == NULL ) { + fprintf( stderr, + _("missing specification of vlv control\n") ); + exit( EXIT_FAILURE ); + } + if ( parse_vlv( cvalue )) + exit( EXIT_FAILURE ); + + vlv = 1 + crit; + +#ifdef LDAP_CONTROL_X_DEREF + } else if ( strcasecmp( control, "deref" ) == 0 ) { + int ispecs; + char **specs; + + /* cvalue is something like + * + * derefAttr:attr[,attr[...]][;derefAttr:attr[,attr[...]]]" + */ + + specs = ldap_str2charray( cvalue, ";" ); + if ( specs == NULL ) { + fprintf( stderr, _("deref specs \"%s\" invalid\n"), + cvalue ); + exit( EXIT_FAILURE ); + } + for ( ispecs = 0; specs[ ispecs ] != NULL; ispecs++ ) + /* count'em */ ; + + ds = ldap_memcalloc( ispecs + 1, sizeof( LDAPDerefSpec ) ); + if ( ds == NULL ) { + perror( "malloc" ); + exit( EXIT_FAILURE ); + } + + for ( ispecs = 0; specs[ ispecs ] != NULL; ispecs++ ) { + char *ptr; + + ptr = strchr( specs[ ispecs ], ':' ); + if ( ptr == NULL ) { + fprintf( stderr, _("deref specs \"%s\" invalid\n"), + cvalue ); + exit( EXIT_FAILURE ); + } + + ds[ ispecs ].derefAttr = specs[ ispecs ]; + *ptr++ = '\0'; + ds[ ispecs ].attributes = ldap_str2charray( ptr, "," ); + } + + derefcrit = 1 + crit; + + ldap_memfree( specs ); +#endif /* LDAP_CONTROL_X_DEREF */ + + } else if ( tool_is_oid( control ) ) { + if ( ctrl_add() ) { + exit( EXIT_FAILURE ); + } + + /* OID */ + c[ nctrls - 1 ].ldctl_oid = control; + + /* value */ + if ( cvalue == NULL ) { + c[ nctrls - 1 ].ldctl_value.bv_val = NULL; + c[ nctrls - 1 ].ldctl_value.bv_len = 0; + + } else if ( cvalue[ 0 ] == ':' ) { + struct berval type; + struct berval value; + int freeval; + char save_c; + + cvalue++; + + /* dummy type "x" + * to use ldif_parse_line2() */ + save_c = cvalue[ -2 ]; + cvalue[ -2 ] = 'x'; + ldif_parse_line2( &cvalue[ -2 ], &type, + &value, &freeval ); + cvalue[ -2 ] = save_c; + + if ( freeval ) { + c[ nctrls - 1 ].ldctl_value = value; + + } else { + ber_dupbv( &c[ nctrls - 1 ].ldctl_value, &value ); + } + + } else { + fprintf( stderr, "unable to parse %s control value\n", control ); + exit( EXIT_FAILURE ); + + } + + /* criticality */ + c[ nctrls - 1 ].ldctl_iscritical = crit; + + } else { + fprintf( stderr, _("Invalid search extension name: %s\n"), + control ); + usage(); + } + break; + case 'F': /* uri prefix */ + if( urlpre ) free( urlpre ); + urlpre = strdup( optarg ); + break; + case 'l': /* time limit */ + if ( strcasecmp( optarg, "none" ) == 0 ) { + timelimit = 0; + + } else if ( strcasecmp( optarg, "max" ) == 0 ) { + timelimit = LDAP_MAXINT; + + } else { + ival = strtol( optarg, &next, 10 ); + if ( next == NULL || next[0] != '\0' ) { + fprintf( stderr, + _("Unable to parse time limit \"%s\"\n"), optarg ); + exit( EXIT_FAILURE ); + } + timelimit = ival; + } + if( timelimit < 0 || timelimit > LDAP_MAXINT ) { + fprintf( stderr, _("%s: invalid timelimit (%d) specified\n"), + prog, timelimit ); + exit( EXIT_FAILURE ); + } + break; + case 'L': /* print entries in LDIF format */ + ++ldif; + break; + case 's': /* search scope */ + if ( strncasecmp( optarg, "base", sizeof("base")-1 ) == 0 ) { + scope = LDAP_SCOPE_BASE; + } else if ( strncasecmp( optarg, "one", sizeof("one")-1 ) == 0 ) { + scope = LDAP_SCOPE_ONELEVEL; + } else if (( strcasecmp( optarg, "subordinate" ) == 0 ) + || ( strcasecmp( optarg, "children" ) == 0 )) + { + scope = LDAP_SCOPE_SUBORDINATE; + } else if ( strncasecmp( optarg, "sub", sizeof("sub")-1 ) == 0 ) { + scope = LDAP_SCOPE_SUBTREE; + } else { + fprintf( stderr, _("scope should be base, one, or sub\n") ); + usage(); + } + break; + case 'S': /* sort attribute */ + sortattr = strdup( optarg ); + break; + case 't': /* write attribute values to TMPDIR files */ + ++vals2tmp; + break; + case 'T': /* tmpdir */ + if( tmpdir ) free( tmpdir ); + tmpdir = strdup( optarg ); + break; + case 'u': /* include UFN */ + ++includeufn; + break; + case 'z': /* size limit */ + if ( strcasecmp( optarg, "none" ) == 0 ) { + sizelimit = 0; + + } else if ( strcasecmp( optarg, "max" ) == 0 ) { + sizelimit = LDAP_MAXINT; + + } else { + ival = strtol( optarg, &next, 10 ); + if ( next == NULL || next[0] != '\0' ) { + fprintf( stderr, + _("Unable to parse size limit \"%s\"\n"), optarg ); + exit( EXIT_FAILURE ); + } + sizelimit = ival; + } + if( sizelimit < 0 || sizelimit > LDAP_MAXINT ) { + fprintf( stderr, _("%s: invalid sizelimit (%d) specified\n"), + prog, sizelimit ); + exit( EXIT_FAILURE ); + } + break; + default: + return 0; + } + return 1; +} + + +static void +private_conn_setup( LDAP *ld ) +{ + if (deref != -1 && + ldap_set_option( ld, LDAP_OPT_DEREF, (void *) &deref ) + != LDAP_OPT_SUCCESS ) + { + fprintf( stderr, _("Could not set LDAP_OPT_DEREF %d\n"), deref ); + tool_exit( ld, EXIT_FAILURE ); + } +} + +int +main( int argc, char **argv ) +{ + char *filtpattern, **attrs = NULL, line[BUFSIZ]; + FILE *fp = NULL; + int rc, rc1, i, first; + LDAP *ld = NULL; + BerElement *seber = NULL, *vrber = NULL; + + BerElement *syncber = NULL; + struct berval *syncbvalp = NULL; + int err; + + tool_init( TOOL_SEARCH ); + + npagedresponses = npagedentries = npagedreferences = + npagedextended = npagedpartial = 0; + + prog = lutil_progname( "ldapsearch", argc, argv ); + + if((def_tmpdir = getenv("TMPDIR")) == NULL && + (def_tmpdir = getenv("TMP")) == NULL && + (def_tmpdir = getenv("TEMP")) == NULL ) + { + def_tmpdir = LDAP_TMPDIR; + } + + if ( !*def_tmpdir ) + def_tmpdir = LDAP_TMPDIR; + + def_urlpre = malloc( sizeof("file:////") + strlen(def_tmpdir) ); + + if( def_urlpre == NULL ) { + perror( "malloc" ); + return EXIT_FAILURE; + } + + sprintf( def_urlpre, "file:///%s/", + def_tmpdir[0] == *LDAP_DIRSEP ? &def_tmpdir[1] : def_tmpdir ); + + urlize( def_urlpre ); + + tool_args( argc, argv ); + + if ( vlv && !sss ) { + fprintf( stderr, + _("VLV control requires server side sort control\n" )); + return EXIT_FAILURE; + } + + if (( argc - optind < 1 ) || + ( *argv[optind] != '(' /*')'*/ && + ( strchr( argv[optind], '=' ) == NULL ) ) ) + { + filtpattern = "(objectclass=*)"; + } else { + filtpattern = argv[optind++]; + } + + if ( argv[optind] != NULL ) { + attrs = &argv[optind]; + } + + if ( infile != NULL ) { + int percent = 0; + + if ( infile[0] == '-' && infile[1] == '\0' ) { + fp = stdin; + } else if (( fp = fopen( infile, "r" )) == NULL ) { + perror( infile ); + return EXIT_FAILURE; + } + + for( i=0 ; filtpattern[i] ; i++ ) { + if( filtpattern[i] == '%' ) { + if( percent ) { + fprintf( stderr, _("Bad filter pattern \"%s\"\n"), + filtpattern ); + return EXIT_FAILURE; + } + + percent++; + + if( filtpattern[i+1] != 's' ) { + fprintf( stderr, _("Bad filter pattern \"%s\"\n"), + filtpattern ); + return EXIT_FAILURE; + } + } + } + } + + if ( tmpdir == NULL ) { + tmpdir = def_tmpdir; + + if ( urlpre == NULL ) + urlpre = def_urlpre; + } + + if( urlpre == NULL ) { + urlpre = malloc( sizeof("file:////") + strlen(tmpdir) ); + + if( urlpre == NULL ) { + perror( "malloc" ); + return EXIT_FAILURE; + } + + sprintf( urlpre, "file:///%s/", + tmpdir[0] == *LDAP_DIRSEP ? &tmpdir[1] : tmpdir ); + + urlize( urlpre ); + } + + if ( debug ) + ldif_debug = debug; + + ld = tool_conn_setup( 0, &private_conn_setup ); + + tool_bind( ld ); + +getNextPage: + /* fp may have been closed, need to reopen if code jumps + * back here to getNextPage. + */ + if ( !fp && infile ) { + if (( fp = fopen( infile, "r" )) == NULL ) { + perror( infile ); + tool_exit( ld, EXIT_FAILURE ); + } + } + save_nctrls = nctrls; + i = nctrls; + if ( nctrls > 0 +#ifdef LDAP_CONTROL_DONTUSECOPY + || dontUseCopy +#endif +#ifdef LDAP_CONTROL_X_DEREF + || derefcrit +#endif + || domainScope + || pagedResults + || ldapsync + || sss + || subentries + || valuesReturnFilter + || vlv ) + { + +#ifdef LDAP_CONTROL_DONTUSECOPY + if ( dontUseCopy ) { + if ( ctrl_add() ) { + tool_exit( ld, EXIT_FAILURE ); + } + + c[i].ldctl_oid = LDAP_CONTROL_DONTUSECOPY; + c[i].ldctl_value.bv_val = NULL; + c[i].ldctl_value.bv_len = 0; + c[i].ldctl_iscritical = dontUseCopy > 1; + i++; + } +#endif + + if ( domainScope ) { + if ( ctrl_add() ) { + tool_exit( ld, EXIT_FAILURE ); + } + + c[i].ldctl_oid = LDAP_CONTROL_X_DOMAIN_SCOPE; + c[i].ldctl_value.bv_val = NULL; + c[i].ldctl_value.bv_len = 0; + c[i].ldctl_iscritical = domainScope > 1; + i++; + } + + if ( subentries ) { + if ( ctrl_add() ) { + tool_exit( ld, EXIT_FAILURE ); + } + + if (( seber = ber_alloc_t(LBER_USE_DER)) == NULL ) { + tool_exit( ld, EXIT_FAILURE ); + } + + err = ber_printf( seber, "b", abs(subentries) == 1 ? 0 : 1 ); + if ( err == -1 ) { + ber_free( seber, 1 ); + fprintf( stderr, _("Subentries control encoding error!\n") ); + tool_exit( ld, EXIT_FAILURE ); + } + + if ( ber_flatten2( seber, &c[i].ldctl_value, 0 ) == -1 ) { + tool_exit( ld, EXIT_FAILURE ); + } + + c[i].ldctl_oid = LDAP_CONTROL_SUBENTRIES; + c[i].ldctl_iscritical = subentries < 1; + i++; + } + + if ( ldapsync ) { + if ( ctrl_add() ) { + tool_exit( ld, EXIT_FAILURE ); + } + + if (( syncber = ber_alloc_t(LBER_USE_DER)) == NULL ) { + tool_exit( ld, EXIT_FAILURE ); + } + + if ( sync_cookie.bv_len == 0 ) { + err = ber_printf( syncber, "{e}", abs(ldapsync) ); + } else { + err = ber_printf( syncber, "{eO}", abs(ldapsync), + &sync_cookie ); + } + + if ( err == -1 ) { + ber_free( syncber, 1 ); + fprintf( stderr, _("ldap sync control encoding error!\n") ); + tool_exit( ld, EXIT_FAILURE ); + } + + if ( ber_flatten( syncber, &syncbvalp ) == -1 ) { + tool_exit( ld, EXIT_FAILURE ); + } + + c[i].ldctl_oid = LDAP_CONTROL_SYNC; + c[i].ldctl_value = (*syncbvalp); + c[i].ldctl_iscritical = ldapsync < 0; + i++; + } + + if ( valuesReturnFilter ) { + if ( ctrl_add() ) { + tool_exit( ld, EXIT_FAILURE ); + } + + if (( vrber = ber_alloc_t(LBER_USE_DER)) == NULL ) { + tool_exit( ld, EXIT_FAILURE ); + } + + if ( ( err = ldap_put_vrFilter( vrber, vrFilter ) ) == -1 ) { + ber_free( vrber, 1 ); + fprintf( stderr, _("Bad ValuesReturnFilter: %s\n"), vrFilter ); + tool_exit( ld, EXIT_FAILURE ); + } + + if ( ber_flatten2( vrber, &c[i].ldctl_value, 0 ) == -1 ) { + tool_exit( ld, EXIT_FAILURE ); + } + + c[i].ldctl_oid = LDAP_CONTROL_VALUESRETURNFILTER; + c[i].ldctl_iscritical = valuesReturnFilter > 1; + i++; + } + + if ( pagedResults ) { + if ( ctrl_add() ) { + tool_exit( ld, EXIT_FAILURE ); + } + + if ( ldap_create_page_control_value( ld, + pageSize, &pr_cookie, &c[i].ldctl_value ) ) + { + tool_exit( ld, EXIT_FAILURE ); + } + + if ( pr_cookie.bv_val != NULL ) { + ber_memfree( pr_cookie.bv_val ); + pr_cookie.bv_val = NULL; + pr_cookie.bv_len = 0; + } + + c[i].ldctl_oid = LDAP_CONTROL_PAGEDRESULTS; + c[i].ldctl_iscritical = pagedResults > 1; + i++; + } + + if ( sss ) { + if ( ctrl_add() ) { + tool_exit( ld, EXIT_FAILURE ); + } + + if ( ldap_create_sort_control_value( ld, + sss_keys, &c[i].ldctl_value ) ) + { + tool_exit( ld, EXIT_FAILURE ); + } + + c[i].ldctl_oid = LDAP_CONTROL_SORTREQUEST; + c[i].ldctl_iscritical = sss > 1; + i++; + } + + if ( vlv ) { + if ( ctrl_add() ) { + tool_exit( ld, EXIT_FAILURE ); + } + + if ( ldap_create_vlv_control_value( ld, + &vlvInfo, &c[i].ldctl_value ) ) + { + tool_exit( ld, EXIT_FAILURE ); + } + + c[i].ldctl_oid = LDAP_CONTROL_VLVREQUEST; + c[i].ldctl_iscritical = sss > 1; + i++; + } +#ifdef LDAP_CONTROL_X_DEREF + if ( derefcrit ) { + if ( derefval.bv_val == NULL ) { + int i; + + assert( ds != NULL ); + + if ( ldap_create_deref_control_value( ld, ds, &derefval ) != LDAP_SUCCESS ) { + tool_exit( ld, EXIT_FAILURE ); + } + + for ( i = 0; ds[ i ].derefAttr != NULL; i++ ) { + ldap_memfree( ds[ i ].derefAttr ); + ldap_charray_free( ds[ i ].attributes ); + } + ldap_memfree( ds ); + ds = NULL; + } + + if ( ctrl_add() ) { + tool_exit( ld, EXIT_FAILURE ); + } + + c[ i ].ldctl_iscritical = derefcrit > 1; + c[ i ].ldctl_oid = LDAP_CONTROL_X_DEREF; + c[ i ].ldctl_value = derefval; + i++; + } +#endif /* LDAP_CONTROL_X_DEREF */ + } + + tool_server_controls( ld, c, i ); + + if ( seber ) ber_free( seber, 1 ); + if ( vrber ) ber_free( vrber, 1 ); + + /* step back to the original number of controls, so that + * those set while parsing args are preserved */ + nctrls = save_nctrls; + + if ( verbose ) { + fprintf( stderr, _("filter%s: %s\nrequesting: "), + infile != NULL ? _(" pattern") : "", + filtpattern ); + + if ( attrs == NULL ) { + fprintf( stderr, _("All userApplication attributes") ); + } else { + for ( i = 0; attrs[ i ] != NULL; ++i ) { + fprintf( stderr, "%s ", attrs[ i ] ); + } + } + fprintf( stderr, "\n" ); + } + + if ( ldif == 0 ) { + printf( _("# extended LDIF\n") ); + } else if ( ldif < 3 ) { + printf( _("version: %d\n\n"), 1 ); + } + + if (ldif < 2 ) { + char *realbase = base; + + if ( realbase == NULL ) { + ldap_get_option( ld, LDAP_OPT_DEFBASE, (void **)(char *)&realbase ); + } + + printf( "#\n" ); + printf(_("# LDAPv%d\n"), protocol); + printf(_("# base <%s>%s with scope %s\n"), + realbase ? realbase : "", + ( realbase == NULL || realbase != base ) ? " (default)" : "", + ((scope == LDAP_SCOPE_BASE) ? "baseObject" + : ((scope == LDAP_SCOPE_ONELEVEL) ? "oneLevel" + : ((scope == LDAP_SCOPE_SUBORDINATE) ? "children" + : "subtree" )))); + printf(_("# filter%s: %s\n"), infile != NULL ? _(" pattern") : "", + filtpattern); + printf(_("# requesting: ")); + + if ( attrs == NULL ) { + printf( _("ALL") ); + } else { + for ( i = 0; attrs[ i ] != NULL; ++i ) { + printf( "%s ", attrs[ i ] ); + } + } + + if ( manageDSAit ) { + printf(_("\n# with manageDSAit %scontrol"), + manageDSAit > 1 ? _("critical ") : "" ); + } + if ( noop ) { + printf(_("\n# with noop %scontrol"), + noop > 1 ? _("critical ") : "" ); + } + if ( subentries ) { + printf(_("\n# with subentries %scontrol: %s"), + subentries < 0 ? _("critical ") : "", + abs(subentries) == 1 ? "false" : "true" ); + } + if ( valuesReturnFilter ) { + printf(_("\n# with valuesReturnFilter %scontrol: %s"), + valuesReturnFilter > 1 ? _("critical ") : "", vrFilter ); + } + if ( pagedResults ) { + printf(_("\n# with pagedResults %scontrol: size=%d"), + (pagedResults > 1) ? _("critical ") : "", + pageSize ); + } + if ( sss ) { + printf(_("\n# with server side sorting %scontrol"), + sss > 1 ? _("critical ") : "" ); + } + if ( vlv ) { + printf(_("\n# with virtual list view %scontrol: %d/%d"), + vlv > 1 ? _("critical ") : "", + vlvInfo.ldvlv_before_count, vlvInfo.ldvlv_after_count); + if ( vlvInfo.ldvlv_attrvalue ) + printf(":%s", vlvInfo.ldvlv_attrvalue->bv_val ); + else + printf("/%d/%d", vlvInfo.ldvlv_offset, vlvInfo.ldvlv_count ); + } +#ifdef LDAP_CONTROL_X_DEREF + if ( derefcrit ) { + printf(_("\n# with dereference %scontrol"), + derefcrit > 1 ? _("critical ") : "" ); + } +#endif + + printf( _("\n#\n\n") ); + + if ( realbase && realbase != base ) { + ldap_memfree( realbase ); + } + } + + if ( infile == NULL ) { + rc = dosearch( ld, base, scope, NULL, filtpattern, + attrs, attrsonly, NULL, NULL, NULL, sizelimit ); + + } else { + rc = 0; + first = 1; + while ( fgets( line, sizeof( line ), fp ) != NULL ) { + line[ strlen( line ) - 1 ] = '\0'; + if ( !first ) { + putchar( '\n' ); + } else { + first = 0; + } + rc1 = dosearch( ld, base, scope, filtpattern, line, + attrs, attrsonly, NULL, NULL, NULL, sizelimit ); + + if ( rc1 != 0 ) { + rc = rc1; + if ( !contoper ) + break; + } + } + if ( fp != stdin ) { + fclose( fp ); + fp = NULL; + } + } + + if (( rc == LDAP_SUCCESS ) && pageSize && pr_morePagedResults ) { + char buf[12]; + int i, moreEntries, tmpSize; + + /* Loop to get the next pages when + * enter is pressed on the terminal. + */ + if ( pagePrompt != 0 ) { + if ( entriesLeft > 0 ) { + printf( _("Estimate entries: %d\n"), entriesLeft ); + } + printf( _("Press [size] Enter for the next {%d|size} entries.\n"), + (int)pageSize ); + i = 0; + moreEntries = getchar(); + while ( moreEntries != EOF && moreEntries != '\n' ) { + if ( i < (int)sizeof(buf) - 1 ) { + buf[i] = moreEntries; + i++; + } + moreEntries = getchar(); + } + buf[i] = '\0'; + + if ( i > 0 && isdigit( (unsigned char)buf[0] ) ) { + int num = sscanf( buf, "%d", &tmpSize ); + if ( num != 1 ) { + fprintf( stderr, + _("Invalid value for PagedResultsControl, %s.\n"), buf); + tool_exit( ld, EXIT_FAILURE ); + + } + pageSize = (ber_int_t)tmpSize; + } + } + + goto getNextPage; + } + + if (( rc == LDAP_SUCCESS ) && vlv ) { + char buf[BUFSIZ]; + int i, moreEntries; + + /* Loop to get the next window when + * enter is pressed on the terminal. + */ + printf( _("Press [before/after(/offset/count|:value)] Enter for the next window.\n")); + i = 0; + moreEntries = getchar(); + while ( moreEntries != EOF && moreEntries != '\n' ) { + if ( i < (int)sizeof(buf) - 1 ) { + buf[i] = moreEntries; + i++; + } + moreEntries = getchar(); + } + buf[i] = '\0'; + if ( buf[0] ) { + i = parse_vlv( strdup( buf )); + if ( i ) + tool_exit( ld, EXIT_FAILURE ); + } else { + vlvInfo.ldvlv_attrvalue = NULL; + vlvInfo.ldvlv_count = vlvCount; + vlvInfo.ldvlv_offset += vlvInfo.ldvlv_after_count; + } + + if ( vlvInfo.ldvlv_context ) + ber_bvfree( vlvInfo.ldvlv_context ); + vlvInfo.ldvlv_context = vlvContext; + + goto getNextPage; + } + + if ( base != NULL ) { + ber_memfree( base ); + } + if ( control != NULL ) { + ber_memfree( control ); + } + if ( sss_keys != NULL ) { + ldap_free_sort_keylist( sss_keys ); + } + if ( derefval.bv_val != NULL ) { + ldap_memfree( derefval.bv_val ); + } + if ( urlpre != NULL ) { + if ( def_urlpre != urlpre ) + free( def_urlpre ); + free( urlpre ); + } + + if ( c ) { + for ( ; save_nctrls-- > 0; ) { + ber_memfree( c[ save_nctrls ].ldctl_value.bv_val ); + } + free( c ); + c = NULL; + } + + tool_exit( ld, rc ); +} + + +static int dosearch( + LDAP *ld, + char *base, + int scope, + char *filtpatt, + char *value, + char **attrs, + int attrsonly, + LDAPControl **sctrls, + LDAPControl **cctrls, + struct timeval *timeout, + int sizelimit ) +{ + char *filter; + int rc, rc2 = LDAP_OTHER; + int nresponses; + int nentries; + int nreferences; + int nextended; + int npartial; + LDAPMessage *res, *msg; + ber_int_t msgid; + char *retoid = NULL; + struct berval *retdata = NULL; + int nresponses_psearch = -1; + int cancel_msgid = -1; + struct timeval tv, *tvp = NULL; + struct timeval tv_timelimit, *tv_timelimitp = NULL; + + if( filtpatt != NULL ) { + size_t max_fsize = strlen( filtpatt ) + strlen( value ) + 1, outlen; + filter = malloc( max_fsize ); + if( filter == NULL ) { + perror( "malloc" ); + return EXIT_FAILURE; + } + + outlen = snprintf( filter, max_fsize, filtpatt, value ); + if( outlen >= max_fsize ) { + fprintf( stderr, "Bad filter pattern: \"%s\"\n", filtpatt ); + free( filter ); + return EXIT_FAILURE; + } + + if ( verbose ) { + fprintf( stderr, _("filter: %s\n"), filter ); + } + + if( ldif < 2 ) { + printf( _("#\n# filter: %s\n#\n"), filter ); + } + + } else { + filter = value; + } + + if ( dont ) { + if ( filtpatt != NULL ) { + free( filter ); + } + return LDAP_SUCCESS; + } + + if ( timelimit > 0 ) { + tv_timelimit.tv_sec = timelimit; + tv_timelimit.tv_usec = 0; + tv_timelimitp = &tv_timelimit; + } + +again: + rc = ldap_search_ext( ld, base, scope, filter, attrs, attrsonly, + sctrls, cctrls, tv_timelimitp, sizelimit, &msgid ); + + if ( filtpatt != NULL ) { + free( filter ); + } + + if( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_search_ext", rc, NULL, NULL, NULL, NULL ); + return( rc ); + } + + nresponses = nentries = nreferences = nextended = npartial = 0; + + res = NULL; + + if ( timelimit > 0 ) { + /* disable timeout */ + tv.tv_sec = -1; + tv.tv_usec = 0; + tvp = &tv; + } + + if ( backlog == 1 ) { + printf( _("\nWaiting for responses to accumulate, press Enter to continue: ")); + fflush( stdout ); + getchar(); + printf( _("Abandoning msgid %d\n"), msgid ); + ldap_abandon_ext( ld, msgid, NULL, NULL ); + /* turn off syncrepl control */ + ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, NULL ); + backlog = 2; + scope = LDAP_SCOPE_BASE; + goto again; + } else if ( backlog == 2 ) { + tv.tv_sec = timelimit; + } + + while ((rc = ldap_result( ld, LDAP_RES_ANY, + sortattr ? LDAP_MSG_ALL : LDAP_MSG_ONE, + tvp, &res )) > 0 ) + { + if ( tool_check_abandon( ld, msgid ) ) { + return -1; + } + + if( sortattr ) { + (void) ldap_sort_entries( ld, &res, + ( *sortattr == '\0' ) ? NULL : sortattr, strcasecmp ); + } + + for ( msg = ldap_first_message( ld, res ); + msg != NULL; + msg = ldap_next_message( ld, msg ) ) + { + if ( nresponses++ ) putchar('\n'); + if ( nresponses_psearch >= 0 ) + nresponses_psearch++; + + switch( ldap_msgtype( msg ) ) { + case LDAP_RES_SEARCH_ENTRY: + nentries++; + print_entry( ld, msg, attrsonly ); + break; + + case LDAP_RES_SEARCH_REFERENCE: + nreferences++; + print_reference( ld, msg ); + break; + + case LDAP_RES_EXTENDED: + nextended++; + print_extended( ld, msg ); + + if ( ldap_msgid( msg ) == 0 ) { + /* unsolicited extended operation */ + goto done; + } + + if ( cancel_msgid != -1 && + cancel_msgid == ldap_msgid( msg ) ) { + printf(_("Cancelled \n")); + printf(_("cancel_msgid = %d\n"), cancel_msgid); + goto done; + } + break; + + case LDAP_RES_SEARCH_RESULT: + /* pagedResults stuff is dealt with + * in tool_print_ctrls(), called by + * print_results(). */ + rc2 = print_result( ld, msg, 1 ); + if ( ldapsync == LDAP_SYNC_REFRESH_AND_PERSIST ) { + break; + } + + goto done; + + case LDAP_RES_INTERMEDIATE: + npartial++; + ldap_parse_intermediate( ld, msg, + &retoid, &retdata, NULL, 0 ); + + nresponses_psearch = 0; + + if ( strcmp( retoid, LDAP_SYNC_INFO ) == 0 ) { + printf(_("# SyncInfo Received\n")); + ldap_memfree( retoid ); + ber_bvfree( retdata ); + break; + } + + print_partial( ld, msg ); + ldap_memfree( retoid ); + ber_bvfree( retdata ); + goto done; + } + + if ( ldapsync && sync_slimit != -1 && + nresponses_psearch >= sync_slimit ) { + BerElement *msgidber = NULL; + struct berval *msgidvalp = NULL; + msgidber = ber_alloc_t(LBER_USE_DER); + ber_printf(msgidber, "{i}", msgid); + ber_flatten(msgidber, &msgidvalp); + ldap_extended_operation(ld, LDAP_EXOP_CANCEL, + msgidvalp, NULL, NULL, &cancel_msgid); + nresponses_psearch = -1; + } + } + + ldap_msgfree( res ); + fflush( stdout ); + } + +done: + if ( tvp == NULL && rc != LDAP_RES_SEARCH_RESULT ) { + ldap_get_option( ld, LDAP_OPT_RESULT_CODE, (void *)&rc2 ); + } + + ldap_msgfree( res ); + + if ( pagedResults ) { + npagedresponses += nresponses; + npagedentries += nentries; + npagedextended += nextended; + npagedpartial += npartial; + npagedreferences += nreferences; + if ( ( pr_morePagedResults == 0 ) && ( ldif < 2 ) ) { + printf( _("\n# numResponses: %d\n"), npagedresponses ); + if( npagedentries ) { + printf( _("# numEntries: %d\n"), npagedentries ); + } + if( npagedextended ) { + printf( _("# numExtended: %d\n"), npagedextended ); + } + if( npagedpartial ) { + printf( _("# numPartial: %d\n"), npagedpartial ); + } + if( npagedreferences ) { + printf( _("# numReferences: %d\n"), npagedreferences ); + } + } + } else if ( ldif < 2 ) { + printf( _("\n# numResponses: %d\n"), nresponses ); + if( nentries ) printf( _("# numEntries: %d\n"), nentries ); + if( nextended ) printf( _("# numExtended: %d\n"), nextended ); + if( npartial ) printf( _("# numPartial: %d\n"), npartial ); + if( nreferences ) printf( _("# numReferences: %d\n"), nreferences ); + } + + if ( rc != LDAP_RES_SEARCH_RESULT ) { + tool_perror( "ldap_result", rc2, NULL, NULL, NULL, NULL ); + } + + return( rc2 ); +} + +/* This is the proposed new way of doing things. + * It is more efficient, but the API is non-standard. + */ +static void +print_entry( + LDAP *ld, + LDAPMessage *entry, + int attrsonly) +{ + char *ufn = NULL; + char tmpfname[ 256 ]; + char url[ 256 ]; + int i, rc; + BerElement *ber = NULL; + struct berval bv, *bvals, **bvp = &bvals; + LDAPControl **ctrls = NULL; + FILE *tmpfp; + + rc = ldap_get_dn_ber( ld, entry, &ber, &bv ); + + if ( ldif < 2 ) { + ufn = ldap_dn2ufn( bv.bv_val ); + tool_write_ldif( LDIF_PUT_COMMENT, NULL, ufn, ufn ? strlen( ufn ) : 0 ); + } + tool_write_ldif( LDIF_PUT_VALUE, "dn", bv.bv_val, bv.bv_len ); + + rc = ldap_get_entry_controls( ld, entry, &ctrls ); + if( rc != LDAP_SUCCESS ) { + fprintf(stderr, _("print_entry: %d\n"), rc ); + tool_perror( "ldap_get_entry_controls", rc, NULL, NULL, NULL, NULL ); + tool_exit( ld, EXIT_FAILURE ); + } + + if( ctrls ) { + tool_print_ctrls( ld, ctrls ); + ldap_controls_free( ctrls ); + } + + if ( includeufn ) { + if( ufn == NULL ) { + ufn = ldap_dn2ufn( bv.bv_val ); + } + tool_write_ldif( LDIF_PUT_VALUE, "ufn", ufn, ufn ? strlen( ufn ) : 0 ); + } + + if( ufn != NULL ) ldap_memfree( ufn ); + + if ( attrsonly ) bvp = NULL; + + for ( rc = ldap_get_attribute_ber( ld, entry, ber, &bv, bvp ); + rc == LDAP_SUCCESS; + rc = ldap_get_attribute_ber( ld, entry, ber, &bv, bvp ) ) + { + if (bv.bv_val == NULL) break; + + if ( attrsonly ) { + tool_write_ldif( LDIF_PUT_NOVALUE, bv.bv_val, NULL, 0 ); + + } else if ( bvals ) { + for ( i = 0; bvals[i].bv_val != NULL; i++ ) { + if ( vals2tmp > 1 || ( vals2tmp && + ldif_is_not_printable( bvals[i].bv_val, bvals[i].bv_len ))) + { + int tmpfd; + /* write value to file */ + snprintf( tmpfname, sizeof tmpfname, + "%s" LDAP_DIRSEP "ldapsearch-%s-XXXXXX", + tmpdir, bv.bv_val ); + tmpfp = NULL; + + tmpfd = mkstemp( tmpfname ); + + if ( tmpfd < 0 ) { + perror( tmpfname ); + continue; + } + + if (( tmpfp = fdopen( tmpfd, "w")) == NULL ) { + perror( tmpfname ); + continue; + } + + if ( fwrite( bvals[ i ].bv_val, + bvals[ i ].bv_len, 1, tmpfp ) == 0 ) + { + perror( tmpfname ); + fclose( tmpfp ); + continue; + } + + fclose( tmpfp ); + + snprintf( url, sizeof url, "%s%s", urlpre, + &tmpfname[strlen(tmpdir) + sizeof(LDAP_DIRSEP) - 1] ); + + urlize( url ); + tool_write_ldif( LDIF_PUT_URL, bv.bv_val, url, strlen( url )); + + } else { + tool_write_ldif( LDIF_PUT_VALUE, bv.bv_val, + bvals[ i ].bv_val, bvals[ i ].bv_len ); + } + } + ber_memfree( bvals ); + } + } + + if( ber != NULL ) { + ber_free( ber, 0 ); + } +} + +static void print_reference( + LDAP *ld, + LDAPMessage *reference ) +{ + int rc; + char **refs = NULL; + LDAPControl **ctrls; + + if( ldif < 2 ) { + printf(_("# search reference\n")); + } + + rc = ldap_parse_reference( ld, reference, &refs, &ctrls, 0 ); + + if( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_parse_reference", rc, NULL, NULL, NULL, NULL ); + tool_exit( ld, EXIT_FAILURE ); + } + + if( refs ) { + int i; + for( i=0; refs[i] != NULL; i++ ) { + tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE, + "ref", refs[i], strlen(refs[i]) ); + } + ber_memvfree( (void **) refs ); + } + + if( ctrls ) { + tool_print_ctrls( ld, ctrls ); + ldap_controls_free( ctrls ); + } +} + +static void print_extended( + LDAP *ld, + LDAPMessage *extended ) +{ + int rc; + char *retoid = NULL; + struct berval *retdata = NULL; + + if( ldif < 2 ) { + printf(_("# extended result response\n")); + } + + rc = ldap_parse_extended_result( ld, extended, + &retoid, &retdata, 0 ); + + if( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_parse_extended_result", rc, NULL, NULL, NULL, NULL ); + tool_exit( ld, EXIT_FAILURE ); + } + + if ( ldif < 2 ) { + tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE, + "extended", retoid, retoid ? strlen(retoid) : 0 ); + } + ber_memfree( retoid ); + + if(retdata) { + if ( ldif < 2 ) { + tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_BINARY, + "data", retdata->bv_val, retdata->bv_len ); + } + ber_bvfree( retdata ); + } + + print_result( ld, extended, 0 ); +} + +static void print_partial( + LDAP *ld, + LDAPMessage *partial ) +{ + int rc; + char *retoid = NULL; + struct berval *retdata = NULL; + LDAPControl **ctrls = NULL; + + if( ldif < 2 ) { + printf(_("# extended partial response\n")); + } + + rc = ldap_parse_intermediate( ld, partial, + &retoid, &retdata, &ctrls, 0 ); + + if( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_parse_intermediate", rc, NULL, NULL, NULL, NULL ); + tool_exit( ld, EXIT_FAILURE ); + } + + if ( ldif < 2 ) { + tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE, + "partial", retoid, retoid ? strlen(retoid) : 0 ); + } + + ber_memfree( retoid ); + + if( retdata ) { + if ( ldif < 2 ) { + tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_BINARY, + "data", retdata->bv_val, retdata->bv_len ); + } + + ber_bvfree( retdata ); + } + + if( ctrls ) { + tool_print_ctrls( ld, ctrls ); + ldap_controls_free( ctrls ); + } +} + +static int print_result( + LDAP *ld, + LDAPMessage *result, int search ) +{ + int rc; + int err; + char *matcheddn = NULL; + char *text = NULL; + char **refs = NULL; + LDAPControl **ctrls = NULL; + + if( search ) { + if ( ldif < 2 ) { + printf(_("# search result\n")); + } + if ( ldif < 1 ) { + printf("%s: %d\n", _("search"), ldap_msgid(result) ); + } + } + + rc = ldap_parse_result( ld, result, + &err, &matcheddn, &text, &refs, &ctrls, 0 ); + + if( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_parse_result", rc, NULL, NULL, NULL, NULL ); + tool_exit( ld, EXIT_FAILURE ); + } + + + if( !ldif ) { + printf( _("result: %d %s\n"), err, ldap_err2string(err) ); + + } else if ( err != LDAP_SUCCESS ) { + fprintf( stderr, "%s (%d)\n", ldap_err2string(err), err ); + } + + if( matcheddn ) { + if( *matcheddn ) { + if( !ldif ) { + tool_write_ldif( LDIF_PUT_VALUE, + "matchedDN", matcheddn, strlen(matcheddn) ); + } else { + fprintf( stderr, _("Matched DN: %s\n"), matcheddn ); + } + } + + ber_memfree( matcheddn ); + } + + if( text ) { + if( *text ) { + if( !ldif ) { + if ( err == LDAP_PARTIAL_RESULTS ) { + char *line; + + for ( line = text; line != NULL; ) { + char *next = strchr( line, '\n' ); + + tool_write_ldif( LDIF_PUT_TEXT, + "text", line, + next ? (size_t) (next - line) : strlen( line )); + + line = next ? next + 1 : NULL; + } + + } else { + tool_write_ldif( LDIF_PUT_TEXT, "text", + text, strlen(text) ); + } + } else { + fprintf( stderr, _("Additional information: %s\n"), text ); + } + } + + ber_memfree( text ); + } + + if( refs ) { + int i; + for( i=0; refs[i] != NULL; i++ ) { + if( !ldif ) { + tool_write_ldif( LDIF_PUT_VALUE, "ref", refs[i], strlen(refs[i]) ); + } else { + fprintf( stderr, _("Referral: %s\n"), refs[i] ); + } + } + + ber_memvfree( (void **) refs ); + } + + pr_morePagedResults = 0; + + if( ctrls ) { + tool_print_ctrls( ld, ctrls ); + ldap_controls_free( ctrls ); + } + + return err; +} diff --git a/clients/tools/ldapurl.c b/clients/tools/ldapurl.c new file mode 100644 index 0000000..6c5642c --- /dev/null +++ b/clients/tools/ldapurl.c @@ -0,0 +1,305 @@ +/* ldapurl -- a tool for generating LDAP URLs */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2008-2021 The OpenLDAP Foundation. + * Portions Copyright 2008 Pierangelo Masarati, SysNet + * 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-1996 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. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by Pierangelo Masarati + * for inclusion in OpenLDAP software. + */ + +#include "portable.h" + +#include <ac/stdlib.h> +#include <stdio.h> +#include <ac/unistd.h> + +#include "ldap.h" +#include "ldap_pvt.h" +#include "lutil.h" + +static int +usage(void) +{ + fprintf( stderr, _("usage: %s [options]\n\n"), "ldapurl" ); + fprintf( stderr, _("generates RFC 4516 LDAP URL with extensions\n\n" ) ); + fprintf( stderr, _("URL options:\n")); + fprintf( stderr, _(" -a attrs comma separated list of attributes\n" ) ); + fprintf( stderr, _(" -b base (RFC 4514 LDAP DN)\n" ) ); + fprintf( stderr, _(" -E ext (format: \"ext=value\"; multiple occurrences allowed)\n" ) ); + fprintf( stderr, _(" -f filter (RFC 4515 LDAP filter)\n" ) ); + fprintf( stderr, _(" -h host \n" ) ); + fprintf( stderr, _(" -p port (default: 389 for ldap, 636 for ldaps)\n" ) ); + fprintf( stderr, _(" -s scope (RFC 4511 searchScope and extensions)\n" ) ); + fprintf( stderr, _(" -S scheme (RFC 4516 LDAP URL scheme and extensions)\n" ) ); + exit( EXIT_FAILURE ); +} + +static int +do_uri_create( LDAPURLDesc *lud ) +{ + char *uri; + + if ( lud->lud_scheme == NULL ) { + lud->lud_scheme = "ldap"; + } + + if ( lud->lud_port == -1 ) { + if ( strcasecmp( lud->lud_scheme, "ldap" ) == 0 ) { + lud->lud_port = LDAP_PORT; + + } else if ( strcasecmp( lud->lud_scheme, "ldaps" ) == 0 ) { + lud->lud_port = LDAPS_PORT; + + } else if ( strcasecmp( lud->lud_scheme, "ldapi" ) == 0 ) { + lud->lud_port = 0; + + } else { + /* forgiving... */ + lud->lud_port = 0; + } + } + + if ( lud->lud_scope == -1 ) { + lud->lud_scope = LDAP_SCOPE_DEFAULT; + } + + uri = ldap_url_desc2str( lud ); + + if ( lud->lud_attrs != NULL ) { + ldap_charray_free( lud->lud_attrs ); + lud->lud_attrs = NULL; + } + + if ( lud->lud_exts != NULL ) { + free( lud->lud_exts ); + lud->lud_exts = NULL; + } + + if ( uri == NULL ) { + fprintf( stderr, "unable to generate URI\n" ); + exit( EXIT_FAILURE ); + } + + printf( "%s\n", uri ); + free( uri ); + + return 0; +} + +static int +do_uri_explode( const char *uri ) +{ + LDAPURLDesc *lud; + int rc; + + rc = ldap_url_parse( uri, &lud ); + if ( rc != LDAP_URL_SUCCESS ) { + fprintf( stderr, "unable to parse URI \"%s\"\n", uri ); + return 1; + } + + if ( lud->lud_scheme != NULL && lud->lud_scheme[0] != '\0' ) { + printf( "scheme: %s\n", lud->lud_scheme ); + } + + if ( lud->lud_host != NULL && lud->lud_host[0] != '\0' ) { + printf( "host: %s\n", lud->lud_host ); + } + + if ( lud->lud_port != 0 ) { + printf( "port: %d\n", lud->lud_port ); + } + + if ( lud->lud_dn != NULL && lud->lud_dn[0] != '\0' ) { + printf( "dn: %s\n", lud->lud_dn ); + } + + if ( lud->lud_attrs != NULL ) { + int i; + + for ( i = 0; lud->lud_attrs[i] != NULL; i++ ) { + printf( "selector: %s\n", lud->lud_attrs[i] ); + } + } + + if ( lud->lud_scope != LDAP_SCOPE_DEFAULT ) { + printf( "scope: %s\n", ldap_pvt_scope2str( lud->lud_scope ) ); + } + + if ( lud->lud_filter != NULL && lud->lud_filter[0] != '\0' ) { + printf( "filter: %s\n", lud->lud_filter ); + } + + if ( lud->lud_exts != NULL ) { + int i; + + for ( i = 0; lud->lud_exts[i] != NULL; i++ ) { + printf( "extension: %s\n", lud->lud_exts[i] ); + } + } + ldap_free_urldesc( lud ); + + return 0; +} + +int +main( int argc, char *argv[]) +{ + LDAPURLDesc lud = { 0 }; + char *uri = NULL; + int gotlud = 0; + int nexts = 0; + + lud.lud_port = -1; + lud.lud_scope = -1; + + while ( 1 ) { + int opt = getopt( argc, argv, "S:h:p:b:a:s:f:E:H:" ); + + if ( opt == EOF ) { + break; + } + + if ( opt == 'H' ) { + if ( gotlud ) { + fprintf( stderr, "option -H incompatible with previous options\n" ); + usage(); + } + + if ( uri != NULL ) { + fprintf( stderr, "URI already provided\n" ); + usage(); + } + + uri = optarg; + continue; + } + + switch ( opt ) { + case 'S': + case 'h': + case 'p': + case 'b': + case 'a': + case 's': + case 'f': + case 'E': + if ( uri != NULL ) { + fprintf( stderr, "option -%c incompatible with -H\n", opt ); + usage(); + } + gotlud++; + } + + switch ( opt ) { + case 'S': + if ( lud.lud_scheme != NULL ) { + fprintf( stderr, "scheme already provided\n" ); + usage(); + } + lud.lud_scheme = optarg; + break; + + case 'h': + if ( lud.lud_host != NULL ) { + fprintf( stderr, "host already provided\n" ); + usage(); + } + lud.lud_host = optarg; + break; + + case 'p': + if ( lud.lud_port != -1 ) { + fprintf( stderr, "port already provided\n" ); + usage(); + } + + if ( lutil_atoi( &lud.lud_port, optarg ) ) { + fprintf( stderr, "unable to parse port \"%s\"\n", optarg ); + usage(); + } + break; + + case 'b': + if ( lud.lud_dn != NULL ) { + fprintf( stderr, "base already provided\n" ); + usage(); + } + lud.lud_dn = optarg; + break; + + case 'a': + if ( lud.lud_attrs != NULL ) { + fprintf( stderr, "attrs already provided\n" ); + usage(); + } + lud.lud_attrs = ldap_str2charray( optarg, "," ); + if ( lud.lud_attrs == NULL ) { + fprintf( stderr, "unable to parse attrs list \"%s\"\n", optarg ); + usage(); + } + break; + + case 's': + if ( lud.lud_scope != -1 ) { + fprintf( stderr, "scope already provided\n" ); + usage(); + } + + lud.lud_scope = ldap_pvt_str2scope( optarg ); + if ( lud.lud_scope == -1 ) { + fprintf( stderr, "unable to parse scope \"%s\"\n", optarg ); + usage(); + } + break; + + case 'f': + if ( lud.lud_filter != NULL ) { + fprintf( stderr, "filter already provided\n" ); + usage(); + } + lud.lud_filter = optarg; + break; + + case 'E': + lud.lud_exts = (char **)realloc( lud.lud_exts, + sizeof( char * ) * ( nexts + 2 ) ); + lud.lud_exts[ nexts++ ] = optarg; + lud.lud_exts[ nexts ] = NULL; + break; + + default: + assert( opt != 'H' ); + usage(); + } + } + + if ( uri != NULL ) { + return do_uri_explode( uri ); + + } + + return do_uri_create( &lud ); +} diff --git a/clients/tools/ldapwhoami.c b/clients/tools/ldapwhoami.c new file mode 100644 index 0000000..ab238c8 --- /dev/null +++ b/clients/tools/ldapwhoami.c @@ -0,0 +1,235 @@ +/* ldapwhoami.c -- a tool for asking the directory "Who Am I?" */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * Portions Copyright 1998-2003 Kurt D. Zeilenga. + * Portions Copyright 1998-2001 Net Boolean Incorporated. + * Portions Copyright 2001-2003 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) 1992-1996 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. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by Kurt D. Zeilenga for inclusion + * in OpenLDAP Software based, in part, on other client tools. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/ctype.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/unistd.h> + +#include <ldap.h> +#include "lutil.h" +#include "lutil_ldap.h" +#include "ldap_defaults.h" + +#include "common.h" + + +void +usage( void ) +{ + fprintf( stderr, _("Issue LDAP Who am I? operation to request user's authzid\n\n")); + fprintf( stderr, _("usage: %s [options]\n"), prog); + tool_common_usage(); + exit( EXIT_FAILURE ); +} + + +const char options[] = "" + "d:D:e:h:H:InNO:o:p:QR:U:vVw:WxX:y:Y:Z"; + +int +handle_private_option( int i ) +{ + switch ( i ) { +#if 0 + char *control, *cvalue; + int crit; + case 'E': /* whoami extension */ + if( protocol == LDAP_VERSION2 ) { + fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"), + prog, protocol ); + exit( EXIT_FAILURE ); + } + + /* should be extended to support comma separated list of + * [!]key[=value] parameters, e.g. -E !foo,bar=567 + */ + + crit = 0; + cvalue = NULL; + if( optarg[0] == '!' ) { + crit = 1; + optarg++; + } + + control = strdup( optarg ); + if ( (cvalue = strchr( control, '=' )) != NULL ) { + *cvalue++ = '\0'; + } + + fprintf( stderr, _("Invalid whoami extension name: %s\n"), control ); + usage(); +#endif + + default: + return 0; + } + return 1; +} + + +int +main( int argc, char *argv[] ) +{ + int rc; + LDAP *ld = NULL; + char *matcheddn = NULL, *text = NULL, **refs = NULL; + struct berval *authzid = NULL; + int id, code = 0; + LDAPMessage *res = NULL; + LDAPControl **ctrls = NULL; + + tool_init( TOOL_WHOAMI ); + prog = lutil_progname( "ldapwhoami", argc, argv ); + + /* LDAPv3 only */ + protocol = LDAP_VERSION3; + + tool_args( argc, argv ); + + if( argc - optind > 0 ) { + usage(); + } + + ld = tool_conn_setup( 0, 0 ); + + tool_bind( ld ); + + if ( dont ) { + rc = LDAP_SUCCESS; + goto skip; + } + + tool_server_controls( ld, NULL, 0 ); + + rc = ldap_whoami( ld, NULL, NULL, &id ); + + if( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_whoami", rc, NULL, NULL, NULL, NULL ); + rc = EXIT_FAILURE; + goto skip; + } + + for ( ; ; ) { + struct timeval tv; + + if ( tool_check_abandon( ld, id ) ) { + tool_exit( ld, LDAP_CANCELLED ); + } + + tv.tv_sec = 0; + tv.tv_usec = 100000; + + rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res ); + if ( rc < 0 ) { + tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL ); + tool_exit( ld, rc ); + } + + if ( rc != 0 ) { + break; + } + } + + rc = ldap_parse_result( ld, res, + &code, &matcheddn, &text, &refs, &ctrls, 0 ); + + if ( rc == LDAP_SUCCESS ) { + rc = code; + } + + if ( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_parse_result", rc, NULL, matcheddn, text, refs ); + rc = EXIT_FAILURE; + goto skip; + } + + rc = ldap_parse_whoami( ld, res, &authzid ); + + if( rc != LDAP_SUCCESS ) { + tool_perror( "ldap_parse_whoami", rc, NULL, NULL, NULL, NULL ); + rc = EXIT_FAILURE; + goto skip; + } + + if( authzid != NULL ) { + if( authzid->bv_len == 0 ) { + printf(_("anonymous\n") ); + } else { + printf("%s\n", authzid->bv_val ); + } + } + +skip: + ldap_msgfree(res); + if ( verbose || code != LDAP_SUCCESS || + ( matcheddn && *matcheddn ) || ( text && *text ) || refs || ctrls ) + { + printf( _("Result: %s (%d)\n"), ldap_err2string( code ), code ); + + if( text && *text ) { + printf( _("Additional info: %s\n"), text ); + } + + if( matcheddn && *matcheddn ) { + printf( _("Matched DN: %s\n"), matcheddn ); + } + + if( refs ) { + int i; + for( i=0; refs[i]; i++ ) { + printf(_("Referral: %s\n"), refs[i] ); + } + } + + if (ctrls) { + tool_print_ctrls( ld, ctrls ); + ldap_controls_free( ctrls ); + } + } + + ber_memfree( text ); + ber_memfree( matcheddn ); + ber_memvfree( (void **) refs ); + ber_bvfree( authzid ); + + /* disconnect from server */ + tool_exit( ld, code == LDAP_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE ); +} |