1 /* $NetBSD: ldapvc.c,v 1.3 2025/09/05 21:16:13 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-2024 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.3 2025/09/05 21:16:13 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 56 static int req_authzid = 0; 57 static int req_pp = 0; 58 59 #if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL) 60 #define LDAP_SASL_NONE (~0U) 61 static unsigned vc_sasl = LDAP_SASL_NONE; 62 static char *vc_sasl_realm = NULL; 63 static char *vc_sasl_authcid = NULL; 64 static char *vc_sasl_authzid = NULL; 65 static char *vc_sasl_mech = NULL; 66 static char *vc_sasl_secprops = NULL; 67 #endif 68 static char * dn = NULL; 69 static struct berval cred = {0, NULL}; 70 71 void 72 usage( 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 93 const char options[] = "abE:" 94 "d:D:e:H:InNO:o:QR:U:vVw:WxX:y:Y:Z"; 95 96 int 97 handle_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 if (vc_sasl_mech == NULL) { 174 exit(EXIT_FAILURE); 175 } 176 #else 177 #endif 178 179 } else if (strcasecmp(control, "realm") == 0) { 180 #if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL) 181 if (vc_sasl_realm) { 182 fprintf(stderr, 183 _("SASL realm previously specified\n")); 184 exit(EXIT_FAILURE); 185 } 186 if (cvalue == NULL) { 187 fprintf(stderr, 188 _("missing realm in SASL option\n")); 189 exit(EXIT_FAILURE); 190 } 191 192 vc_sasl_realm = ber_strdup(cvalue); 193 if (vc_sasl_realm == NULL) { 194 exit(EXIT_FAILURE); 195 } 196 #else 197 fprintf(stderr, 198 _("%s: not compiled with SASL support\n"), prog); 199 exit(EXIT_FAILURE); 200 #endif 201 202 } else if (strcasecmp(control, "authcid") == 0) { 203 #if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL) 204 if (vc_sasl_authcid) { 205 fprintf(stderr, 206 _("SASL authcid previously specified\n")); 207 exit(EXIT_FAILURE); 208 } 209 if (cvalue == NULL) { 210 fprintf(stderr, 211 _("missing authcid in SASL option\n")); 212 exit(EXIT_FAILURE); 213 } 214 215 vc_sasl_authcid = ber_strdup(cvalue); 216 if (vc_sasl_authcid == NULL) { 217 exit(EXIT_FAILURE); 218 } 219 #else 220 fprintf(stderr, 221 _("%s: not compiled with SASL support\n"), prog); 222 exit(EXIT_FAILURE); 223 #endif 224 225 } else if (strcasecmp(control, "authzid") == 0) { 226 #if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL) 227 if (vc_sasl_authzid) { 228 fprintf(stderr, 229 _("SASL authzid previously specified\n")); 230 exit(EXIT_FAILURE); 231 } 232 if (cvalue == NULL) { 233 fprintf(stderr, 234 _("missing authzid in SASL option\n")); 235 exit(EXIT_FAILURE); 236 } 237 238 vc_sasl_authzid = ber_strdup(cvalue); 239 if (vc_sasl_authzid == NULL) { 240 exit(EXIT_FAILURE); 241 } 242 #else 243 fprintf(stderr, 244 _("%s: not compiled with SASL support\n"), prog); 245 exit(EXIT_FAILURE); 246 #endif 247 248 } else if (strcasecmp(control, "secprops") == 0) { 249 #if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL) 250 if (vc_sasl_secprops) { 251 fprintf(stderr, 252 _("SASL secprops previously specified\n")); 253 exit(EXIT_FAILURE); 254 } 255 if (cvalue == NULL) { 256 fprintf(stderr, 257 _("missing secprops in SASL option\n")); 258 exit(EXIT_FAILURE); 259 } 260 261 vc_sasl_secprops = ber_strdup(cvalue); 262 if (vc_sasl_secprops == NULL) { 263 exit(EXIT_FAILURE); 264 } 265 #else 266 fprintf(stderr, 267 _("%s: not compiled with SASL support\n"), prog); 268 exit(EXIT_FAILURE); 269 #endif 270 271 } else { 272 fprintf( stderr, _("Invalid Verify Credentials extension name: %s\n"), control ); 273 usage(); 274 } 275 break; 276 277 case 'a': /* request authzid */ 278 req_authzid++; 279 break; 280 281 case 'b': /* request authzid */ 282 req_pp++; 283 break; 284 285 default: 286 return 0; 287 } 288 return 1; 289 } 290 291 292 int 293 main( int argc, char *argv[] ) 294 { 295 int rc; 296 LDAP *ld = NULL; 297 char *matcheddn = NULL, *text = NULL, **refs = NULL; 298 int rcode; 299 char * diag = NULL; 300 struct berval *scookie = NULL; 301 struct berval *scred = NULL; 302 int id, code = 0; 303 LDAPMessage *res; 304 LDAPControl **ctrls = NULL; 305 LDAPControl **vcctrls = NULL; 306 int nvcctrls = 0; 307 308 tool_init( TOOL_VC ); 309 prog = lutil_progname( "ldapvc", argc, argv ); 310 311 /* LDAPv3 only */ 312 protocol = LDAP_VERSION3; 313 314 tool_args( argc, argv ); 315 316 if (argc - optind > 0) { 317 dn = argv[optind++]; 318 } 319 if (argc - optind > 0) { 320 cred.bv_val = strdup(argv[optind++]); 321 cred.bv_len = strlen(cred.bv_val); 322 } 323 if (argc - optind > 0) { 324 usage(); 325 } 326 if (dn 327 #ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS_INTERACTIVE 328 && !vc_sasl_mech 329 #endif 330 && !cred.bv_val) 331 { 332 char *userpw = getpassphrase(_("User's password: ")); 333 if ( userpw == NULL ) /* Allow EOF to exit. */ 334 { 335 tool_exit( ld, EXIT_FAILURE ); 336 } 337 cred.bv_val = strdup(userpw); 338 cred.bv_len = strlen(cred.bv_val); 339 } 340 341 #ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS_INTERACTIVE 342 if (vc_sasl_mech && (vc_sasl == LDAP_SASL_NONE)) { 343 vc_sasl = LDAP_SASL_AUTOMATIC; 344 } 345 #endif 346 347 ld = tool_conn_setup( 0, 0 ); 348 349 tool_bind( ld ); 350 351 if ( dont ) { 352 rc = LDAP_SUCCESS; 353 goto skip; 354 } 355 356 tool_server_controls( ld, NULL, 0 ); 357 358 if (req_authzid) { 359 vcctrls = (LDAPControl **) malloc(3*sizeof(LDAPControl *)); 360 vcctrls[nvcctrls] = (LDAPControl *) malloc(sizeof(LDAPControl)); 361 vcctrls[nvcctrls]->ldctl_oid = ldap_strdup(LDAP_CONTROL_AUTHZID_REQUEST); 362 vcctrls[nvcctrls]->ldctl_iscritical = 0; 363 vcctrls[nvcctrls]->ldctl_value.bv_val = NULL; 364 vcctrls[nvcctrls]->ldctl_value.bv_len = 0; 365 vcctrls[++nvcctrls] = NULL; 366 } 367 368 if (req_pp) { 369 if (!vcctrls) vcctrls = (LDAPControl **) malloc(3*sizeof(LDAPControl *)); 370 vcctrls[nvcctrls] = (LDAPControl *) malloc(sizeof(LDAPControl)); 371 vcctrls[nvcctrls]->ldctl_oid = ldap_strdup(LDAP_CONTROL_PASSWORDPOLICYREQUEST); 372 vcctrls[nvcctrls]->ldctl_iscritical = 0; 373 vcctrls[nvcctrls]->ldctl_value.bv_val = NULL; 374 vcctrls[nvcctrls]->ldctl_value.bv_len = 0; 375 vcctrls[++nvcctrls] = NULL; 376 } 377 378 #ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS_INTERACTIVE 379 #ifdef HAVE_CYRUS_SASL 380 if (vc_sasl_mech) { 381 int msgid; 382 void * defaults; 383 void * context = NULL; 384 const char *rmech = NULL; 385 386 defaults = lutil_sasl_defaults(ld, 387 vc_sasl_mech, 388 vc_sasl_realm, 389 vc_sasl_authcid, 390 cred.bv_val, 391 sasl_authz_id); 392 393 do { 394 rc = ldap_verify_credentials_interactive(ld, dn, vc_sasl_mech, 395 vcctrls, NULL, NULL, 396 vc_sasl, lutil_sasl_interact, defaults, context, 397 res, &rmech, &msgid); 398 399 if (rc != LDAP_SASL_BIND_IN_PROGRESS) break; 400 401 ldap_msgfree(res); 402 403 if (ldap_result(ld, msgid, LDAP_MSG_ALL, NULL, &res) == -1 || !res) { 404 ldap_get_option(ld, LDAP_OPT_RESULT_CODE, (void*) &rc); 405 ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*) &text); 406 tool_perror( "ldap_verify_credentials_interactive", rc, NULL, NULL, text, NULL); 407 ldap_memfree(text); 408 tool_exit(ld, rc); 409 } 410 } while (rc == LDAP_SASL_BIND_IN_PROGRESS); 411 412 lutil_sasl_freedefs(defaults); 413 414 if( rc != LDAP_SUCCESS ) { 415 ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*) &text); 416 tool_perror( "ldap_verify_credentials", rc, NULL, NULL, text, NULL ); 417 rc = EXIT_FAILURE; 418 goto skip; 419 } 420 421 } else 422 #endif 423 #endif 424 { 425 rc = ldap_verify_credentials( ld, 426 NULL, 427 dn, NULL, cred.bv_val ? &cred: NULL, vcctrls, 428 NULL, NULL, &id ); 429 430 if( rc != LDAP_SUCCESS ) { 431 ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*) &text); 432 tool_perror( "ldap_verify_credentials", rc, NULL, NULL, text, NULL ); 433 rc = EXIT_FAILURE; 434 goto skip; 435 } 436 437 for ( ; ; ) { 438 struct timeval tv; 439 440 if ( tool_check_abandon( ld, id ) ) { 441 tool_exit( ld, LDAP_CANCELLED ); 442 } 443 444 tv.tv_sec = 0; 445 tv.tv_usec = 100000; 446 447 rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res ); 448 if ( rc < 0 ) { 449 tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL ); 450 tool_exit( ld, rc ); 451 } 452 453 if ( rc != 0 ) { 454 break; 455 } 456 } 457 } 458 459 ldap_controls_free(vcctrls); 460 vcctrls = NULL; 461 462 rc = ldap_parse_result( ld, res, 463 &code, &matcheddn, &text, &refs, &ctrls, 0 ); 464 465 if (rc == LDAP_SUCCESS) rc = code; 466 467 if (rc != LDAP_SUCCESS) { 468 tool_perror( "ldap_parse_result", rc, NULL, matcheddn, text, refs ); 469 rc = EXIT_FAILURE; 470 goto skip; 471 } 472 473 rc = ldap_parse_verify_credentials( ld, res, &rcode, &diag, &scookie, &scred, &vcctrls ); 474 ldap_msgfree(res); 475 476 if (rc != LDAP_SUCCESS) { 477 tool_perror( "ldap_parse_verify_credentials", rc, NULL, NULL, NULL, NULL ); 478 rc = EXIT_FAILURE; 479 goto skip; 480 } 481 482 if (rcode != LDAP_SUCCESS) { 483 printf(_("Failed: %s (%d)\n"), ldap_err2string(rcode), rcode); 484 } 485 486 if (diag && *diag) { 487 printf(_("Diagnostic: %s\n"), diag); 488 } 489 490 if (vcctrls) { 491 tool_print_ctrls( ld, vcctrls ); 492 } 493 494 skip: 495 if ( verbose || code != LDAP_SUCCESS || 496 ( matcheddn && *matcheddn ) || ( text && *text ) || refs || ctrls ) 497 { 498 printf( _("Result: %s (%d)\n"), ldap_err2string( code ), code ); 499 500 if( text && *text ) { 501 printf( _("Additional info: %s\n"), text ); 502 } 503 504 if( matcheddn && *matcheddn ) { 505 printf( _("Matched DN: %s\n"), matcheddn ); 506 } 507 508 if( refs ) { 509 int i; 510 for( i=0; refs[i]; i++ ) { 511 printf(_("Referral: %s\n"), refs[i] ); 512 } 513 } 514 515 if (ctrls) { 516 tool_print_ctrls( ld, ctrls ); 517 ldap_controls_free( ctrls ); 518 } 519 } 520 521 ber_memfree( text ); 522 ber_memfree( matcheddn ); 523 ber_memvfree( (void **) refs ); 524 ber_bvfree( scookie ); 525 ber_bvfree( scred ); 526 ber_memfree( diag ); 527 free( cred.bv_val ); 528 529 /* disconnect from server */ 530 tool_exit( ld, code == LDAP_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE ); 531 } 532