1/* $NetBSD: ldapvc.c,v 1.2 2021/08/14 16:14:49 christos Exp $ */ 2 3/* ldapvc.c -- a tool for verifying credentials */ 4/* $OpenLDAP$ */ 5/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 1998-2021 The OpenLDAP Foundation. 8 * Portions Copyright 2010 Kurt D. Zeilenga. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted only as authorized by the OpenLDAP 13 * Public License. 14 * 15 * A copy of this license is available in the file LICENSE in the 16 * top-level directory of the distribution or, alternatively, at 17 * <http://www.OpenLDAP.org/license.html>. 18 */ 19/* Portions Copyright (c) 1992-1996 Regents of the University of Michigan. 20 * All rights reserved. 21 * 22 * Redistribution and use in source and binary forms are permitted 23 * provided that this notice is preserved and that due credit is given 24 * to the University of Michigan at Ann Arbor. The name of the 25 * University may not be used to endorse or promote products derived 26 * from this software without specific prior written permission. This 27 * software is provided ``as is'' without express or implied warranty. 28 */ 29/* ACKNOWLEDGEMENTS: 30 * This work was originally developed by Kurt D. Zeilenga for inclusion 31 * in OpenLDAP Software based, in part, on other client tools. 32 */ 33 34#include <sys/cdefs.h> 35__RCSID("$NetBSD: ldapvc.c,v 1.2 2021/08/14 16:14:49 christos Exp $"); 36 37#include "portable.h" 38 39#include <stdio.h> 40 41#include <ac/stdlib.h> 42 43#include <ac/ctype.h> 44#include <ac/socket.h> 45#include <ac/string.h> 46#include <ac/time.h> 47#include <ac/unistd.h> 48 49#include <ldap.h> 50#include "lutil.h" 51#include "lutil_ldap.h" 52#include "ldap_defaults.h" 53 54#include "common.h" 55 56static int req_authzid = 0; 57static int req_pp = 0; 58 59#if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL) 60#define LDAP_SASL_NONE (~0U) 61static unsigned vc_sasl = LDAP_SASL_NONE; 62static char *vc_sasl_realm = NULL; 63static char *vc_sasl_authcid = NULL; 64static char *vc_sasl_authzid = NULL; 65static char *vc_sasl_mech = NULL; 66static char *vc_sasl_secprops = NULL; 67#endif 68static char * dn = NULL; 69static struct berval cred = {0, NULL}; 70 71void 72usage( void ) 73{ 74 fprintf( stderr, _("Issue LDAP Verify Credentials operation to verify a user's credentials\n\n")); 75 fprintf( stderr, _("usage: %s [options] [DN [cred]])\n"), prog); 76 fprintf( stderr, _("where:\n")); 77 fprintf( stderr, _(" DN\tDistinguished Name\n")); 78 fprintf( stderr, _(" cred\tCredentials (prompt if not present)\n")); 79 fprintf( stderr, _("options:\n")); 80 fprintf( stderr, _(" -a\tRequest AuthzId\n")); 81 fprintf( stderr, _(" -b\tRequest Password Policy Information\n")); 82 fprintf( stderr, _(" -E sasl=(a[utomatic]|i[nteractive]|q[uiet]>\tSASL mode (defaults to automatic if any other -E option provided, otherwise none))\n")); 83 fprintf( stderr, _(" -E mech=<mech>\tSASL mechanism (default "" e.g. Simple)\n")); 84 fprintf( stderr, _(" -E realm=<realm>\tSASL Realm (defaults to none)\n")); 85 fprintf( stderr, _(" -E authcid=<authcid>\tSASL Authentication Identity (defaults to USER)\n")); 86 fprintf( stderr, _(" -E authzid=<authzid>\tSASL Authorization Identity (defaults to none)\n")); 87 fprintf( stderr, _(" -E secprops=<secprops>\tSASL Security Properties (defaults to none)\n")); 88 tool_common_usage(); 89 exit( EXIT_FAILURE ); 90} 91 92 93const char options[] = "abE:" 94 "d:D:e:h:H:InNO:o:p:QR:U:vVw:WxX:y:Y:Z"; 95 96int 97handle_private_option( int i ) 98{ 99 switch ( i ) { 100 char *control, *cvalue; 101 case 'E': /* vc extension */ 102 if( protocol == LDAP_VERSION2 ) { 103 fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"), 104 prog, protocol ); 105 exit( EXIT_FAILURE ); 106 } 107 108 /* should be extended to support comma separated list of 109 * [!]key[=value] parameters, e.g. -E !foo,bar=567 110 */ 111 112 cvalue = NULL; 113 if( optarg[0] == '!' ) { 114 optarg++; 115 } 116 117 control = optarg; 118 if ( (cvalue = strchr( control, '=' )) != NULL ) { 119 *cvalue++ = '\0'; 120 } 121 122 if (strcasecmp(control, "sasl") == 0) { 123#if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL) 124 if (vc_sasl != LDAP_SASL_NONE) { 125 fprintf(stderr, 126 _("SASL option previously specified\n")); 127 exit(EXIT_FAILURE); 128 } 129 if (cvalue == NULL) { 130 fprintf(stderr, 131 _("missing mode in SASL option\n")); 132 exit(EXIT_FAILURE); 133 } 134 135 switch (*cvalue) { 136 case 'a': 137 case 'A': 138 vc_sasl = LDAP_SASL_AUTOMATIC; 139 break; 140 case 'i': 141 case 'I': 142 vc_sasl = LDAP_SASL_INTERACTIVE; 143 break; 144 case 'q': 145 case 'Q': 146 vc_sasl = LDAP_SASL_QUIET; 147 break; 148 default: 149 fprintf(stderr, 150 _("unknown mode %s in SASL option\n"), cvalue); 151 exit(EXIT_FAILURE); 152 } 153#else 154 fprintf(stderr, 155 _("%s: not compiled with SASL support\n"), prog); 156 exit(EXIT_FAILURE); 157#endif 158 159 } else if (strcasecmp(control, "mech") == 0) { 160#if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL) 161 if (vc_sasl_mech) { 162 fprintf(stderr, 163 _("SASL mech previously specified\n")); 164 exit(EXIT_FAILURE); 165 } 166 if (cvalue == NULL) { 167 fprintf(stderr, 168 _("missing mech in SASL option\n")); 169 exit(EXIT_FAILURE); 170 } 171 172 vc_sasl_mech = ber_strdup(cvalue); 173#else 174#endif 175 176 } else if (strcasecmp(control, "realm") == 0) { 177#if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL) 178 if (vc_sasl_realm) { 179 fprintf(stderr, 180 _("SASL realm previously specified\n")); 181 exit(EXIT_FAILURE); 182 } 183 if (cvalue == NULL) { 184 fprintf(stderr, 185 _("missing realm in SASL option\n")); 186 exit(EXIT_FAILURE); 187 } 188 189 vc_sasl_realm = ber_strdup(cvalue); 190#else 191 fprintf(stderr, 192 _("%s: not compiled with SASL support\n"), prog); 193 exit(EXIT_FAILURE); 194#endif 195 196 } else if (strcasecmp(control, "authcid") == 0) { 197#if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL) 198 if (vc_sasl_authcid) { 199 fprintf(stderr, 200 _("SASL authcid previously specified\n")); 201 exit(EXIT_FAILURE); 202 } 203 if (cvalue == NULL) { 204 fprintf(stderr, 205 _("missing authcid in SASL option\n")); 206 exit(EXIT_FAILURE); 207 } 208 209 vc_sasl_authcid = ber_strdup(cvalue); 210#else 211 fprintf(stderr, 212 _("%s: not compiled with SASL support\n"), prog); 213 exit(EXIT_FAILURE); 214#endif 215 216 } else if (strcasecmp(control, "authzid") == 0) { 217#if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL) 218 if (vc_sasl_authzid) { 219 fprintf(stderr, 220 _("SASL authzid previously specified\n")); 221 exit(EXIT_FAILURE); 222 } 223 if (cvalue == NULL) { 224 fprintf(stderr, 225 _("missing authzid in SASL option\n")); 226 exit(EXIT_FAILURE); 227 } 228 229 vc_sasl_authzid = ber_strdup(cvalue); 230#else 231 fprintf(stderr, 232 _("%s: not compiled with SASL support\n"), prog); 233 exit(EXIT_FAILURE); 234#endif 235 236 } else if (strcasecmp(control, "secprops") == 0) { 237#if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL) 238 if (vc_sasl_secprops) { 239 fprintf(stderr, 240 _("SASL secprops previously specified\n")); 241 exit(EXIT_FAILURE); 242 } 243 if (cvalue == NULL) { 244 fprintf(stderr, 245 _("missing secprops in SASL option\n")); 246 exit(EXIT_FAILURE); 247 } 248 249 vc_sasl_secprops = ber_strdup(cvalue); 250#else 251 fprintf(stderr, 252 _("%s: not compiled with SASL support\n"), prog); 253 exit(EXIT_FAILURE); 254#endif 255 256 } else { 257 fprintf( stderr, _("Invalid Verify Credentials extension name: %s\n"), control ); 258 usage(); 259 } 260 break; 261 262 case 'a': /* request authzid */ 263 req_authzid++; 264 break; 265 266 case 'b': /* request authzid */ 267 req_pp++; 268 break; 269 270 default: 271 return 0; 272 } 273 return 1; 274} 275 276 277int 278main( int argc, char *argv[] ) 279{ 280 int rc; 281 LDAP *ld = NULL; 282 char *matcheddn = NULL, *text = NULL, **refs = NULL; 283 int rcode; 284 char * diag = NULL; 285 struct berval *scookie = NULL; 286 struct berval *scred = NULL; 287 int id, code = 0; 288 LDAPMessage *res; 289 LDAPControl **ctrls = NULL; 290 LDAPControl **vcctrls = NULL; 291 int nvcctrls = 0; 292 293 tool_init( TOOL_VC ); 294 prog = lutil_progname( "ldapvc", argc, argv ); 295 296 /* LDAPv3 only */ 297 protocol = LDAP_VERSION3; 298 299 tool_args( argc, argv ); 300 301 if (argc - optind > 0) { 302 dn = argv[optind++]; 303 } 304 if (argc - optind > 0) { 305 cred.bv_val = strdup(argv[optind++]); 306 cred.bv_len = strlen(cred.bv_val); 307 } 308 if (argc - optind > 0) { 309 usage(); 310 } 311 if (dn 312#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS_INTERACTIVE 313 && !vc_sasl_mech 314#endif 315 && !cred.bv_val) 316 { 317 cred.bv_val = strdup(getpassphrase(_("User's password: "))); 318 cred.bv_len = strlen(cred.bv_val); 319 } 320 321#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS_INTERACTIVE 322 if (vc_sasl_mech && (vc_sasl == LDAP_SASL_NONE)) { 323 vc_sasl = LDAP_SASL_AUTOMATIC; 324 } 325#endif 326 327 ld = tool_conn_setup( 0, 0 ); 328 329 tool_bind( ld ); 330 331 if ( dont ) { 332 rc = LDAP_SUCCESS; 333 goto skip; 334 } 335 336 tool_server_controls( ld, NULL, 0 ); 337 338 if (req_authzid) { 339 vcctrls = (LDAPControl **) malloc(3*sizeof(LDAPControl *)); 340 vcctrls[nvcctrls] = (LDAPControl *) malloc(sizeof(LDAPControl)); 341 vcctrls[nvcctrls]->ldctl_oid = ldap_strdup(LDAP_CONTROL_AUTHZID_REQUEST); 342 vcctrls[nvcctrls]->ldctl_iscritical = 0; 343 vcctrls[nvcctrls]->ldctl_value.bv_val = NULL; 344 vcctrls[nvcctrls]->ldctl_value.bv_len = 0; 345 vcctrls[++nvcctrls] = NULL; 346 } 347 348 if (req_pp) { 349 if (!vcctrls) vcctrls = (LDAPControl **) malloc(3*sizeof(LDAPControl *)); 350 vcctrls[nvcctrls] = (LDAPControl *) malloc(sizeof(LDAPControl)); 351 vcctrls[nvcctrls]->ldctl_oid = ldap_strdup(LDAP_CONTROL_PASSWORDPOLICYREQUEST); 352 vcctrls[nvcctrls]->ldctl_iscritical = 0; 353 vcctrls[nvcctrls]->ldctl_value.bv_val = NULL; 354 vcctrls[nvcctrls]->ldctl_value.bv_len = 0; 355 vcctrls[++nvcctrls] = NULL; 356 } 357 358#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS_INTERACTIVE 359#ifdef HAVE_CYRUS_SASL 360 if (vc_sasl_mech) { 361 int msgid; 362 void * defaults; 363 void * context = NULL; 364 const char *rmech = NULL; 365 366 defaults = lutil_sasl_defaults(ld, 367 vc_sasl_mech, 368 vc_sasl_realm, 369 vc_sasl_authcid, 370 cred.bv_val, 371 sasl_authz_id); 372 373 do { 374 rc = ldap_verify_credentials_interactive(ld, dn, vc_sasl_mech, 375 vcctrls, NULL, NULL, 376 vc_sasl, lutil_sasl_interact, defaults, context, 377 res, &rmech, &msgid); 378 379 if (rc != LDAP_SASL_BIND_IN_PROGRESS) break; 380 381 ldap_msgfree(res); 382 383 if (ldap_result(ld, msgid, LDAP_MSG_ALL, NULL, &res) == -1 || !res) { 384 ldap_get_option(ld, LDAP_OPT_RESULT_CODE, (void*) &rc); 385 ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*) &text); 386 tool_perror( "ldap_verify_credentials_interactive", rc, NULL, NULL, text, NULL); 387 ldap_memfree(text); 388 tool_exit(ld, rc); 389 } 390 } while (rc == LDAP_SASL_BIND_IN_PROGRESS); 391 392 lutil_sasl_freedefs(defaults); 393 394 if( rc != LDAP_SUCCESS ) { 395 ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*) &text); 396 tool_perror( "ldap_verify_credentials", rc, NULL, NULL, text, NULL ); 397 rc = EXIT_FAILURE; 398 goto skip; 399 } 400 401 } else 402#endif 403#endif 404 { 405 rc = ldap_verify_credentials( ld, 406 NULL, 407 dn, NULL, cred.bv_val ? &cred: NULL, vcctrls, 408 NULL, NULL, &id ); 409 410 if( rc != LDAP_SUCCESS ) { 411 ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*) &text); 412 tool_perror( "ldap_verify_credentials", rc, NULL, NULL, text, NULL ); 413 rc = EXIT_FAILURE; 414 goto skip; 415 } 416 417 for ( ; ; ) { 418 struct timeval tv; 419 420 if ( tool_check_abandon( ld, id ) ) { 421 tool_exit( ld, LDAP_CANCELLED ); 422 } 423 424 tv.tv_sec = 0; 425 tv.tv_usec = 100000; 426 427 rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res ); 428 if ( rc < 0 ) { 429 tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL ); 430 tool_exit( ld, rc ); 431 } 432 433 if ( rc != 0 ) { 434 break; 435 } 436 } 437 } 438 439 ldap_controls_free(vcctrls); 440 vcctrls = NULL; 441 442 rc = ldap_parse_result( ld, res, 443 &code, &matcheddn, &text, &refs, &ctrls, 0 ); 444 445 if (rc == LDAP_SUCCESS) rc = code; 446 447 if (rc != LDAP_SUCCESS) { 448 tool_perror( "ldap_parse_result", rc, NULL, matcheddn, text, refs ); 449 rc = EXIT_FAILURE; 450 goto skip; 451 } 452 453 rc = ldap_parse_verify_credentials( ld, res, &rcode, &diag, &scookie, &scred, &vcctrls ); 454 ldap_msgfree(res); 455 456 if (rc != LDAP_SUCCESS) { 457 tool_perror( "ldap_parse_verify_credentials", rc, NULL, NULL, NULL, NULL ); 458 rc = EXIT_FAILURE; 459 goto skip; 460 } 461 462 if (rcode != LDAP_SUCCESS) { 463 printf(_("Failed: %s (%d)\n"), ldap_err2string(rcode), rcode); 464 } 465 466 if (diag && *diag) { 467 printf(_("Diagnostic: %s\n"), diag); 468 } 469 470 if (vcctrls) { 471 tool_print_ctrls( ld, vcctrls ); 472 } 473 474skip: 475 if ( verbose || code != LDAP_SUCCESS || 476 ( matcheddn && *matcheddn ) || ( text && *text ) || refs || ctrls ) 477 { 478 printf( _("Result: %s (%d)\n"), ldap_err2string( code ), code ); 479 480 if( text && *text ) { 481 printf( _("Additional info: %s\n"), text ); 482 } 483 484 if( matcheddn && *matcheddn ) { 485 printf( _("Matched DN: %s\n"), matcheddn ); 486 } 487 488 if( refs ) { 489 int i; 490 for( i=0; refs[i]; i++ ) { 491 printf(_("Referral: %s\n"), refs[i] ); 492 } 493 } 494 495 if (ctrls) { 496 tool_print_ctrls( ld, ctrls ); 497 ldap_controls_free( ctrls ); 498 } 499 } 500 501 ber_memfree( text ); 502 ber_memfree( matcheddn ); 503 ber_memvfree( (void **) refs ); 504 ber_bvfree( scookie ); 505 ber_bvfree( scred ); 506 ber_memfree( diag ); 507 free( cred.bv_val ); 508 509 /* disconnect from server */ 510 tool_exit( ld, code == LDAP_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE ); 511} 512