void do_extended( Slapi_PBlock *pb ) { char *extoid = NULL, *errmsg; struct berval extval = {0}; struct slapdplugin *p = NULL; int lderr, rc; ber_len_t len; ber_tag_t tag; const char *name; slapi_log_err(SLAPI_LOG_TRACE, "do_extended", "->\n"); /* * Parse the extended request. It looks like this: * * ExtendedRequest := [APPLICATION 23] SEQUENCE { * requestName [0] LDAPOID, * requestValue [1] OCTET STRING OPTIONAL * } */ if ( ber_scanf( pb->pb_op->o_ber, "{a", &extoid ) == LBER_ERROR ) { slapi_log_err(SLAPI_LOG_ERR, "do_extended", "ber_scanf failed (op=extended; params=OID)\n"); op_shared_log_error_access (pb, "EXT", "???", "decoding error: fail to get extension OID"); send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, "decoding error", 0, NULL ); goto free_and_return; } tag = ber_peek_tag(pb->pb_op->o_ber, &len); if (tag == LDAP_TAG_EXOP_REQ_VALUE) { if ( ber_scanf( pb->pb_op->o_ber, "o}", &extval ) == LBER_ERROR ) { op_shared_log_error_access (pb, "EXT", "???", "decoding error: fail to get extension value"); send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, "decoding error", 0, NULL ); goto free_and_return; } } else { if ( ber_scanf( pb->pb_op->o_ber, "}") == LBER_ERROR ) { op_shared_log_error_access (pb, "EXT", "???", "decoding error"); send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, "decoding error", 0, NULL ); goto free_and_return; } } if ( NULL == ( name = extended_op_oid2string( extoid ))) { slapi_log_err(SLAPI_LOG_ARGS, "do_extended", "oid (%s)\n", extoid); slapi_log_access( LDAP_DEBUG_STATS, "conn=%" NSPRIu64 " op=%d EXT oid=\"%s\"\n", pb->pb_conn->c_connid, pb->pb_op->o_opid, extoid ); } else { slapi_log_err(SLAPI_LOG_ARGS, "do_extended", "oid (%s-%s)\n", extoid, name); slapi_log_access( LDAP_DEBUG_STATS, "conn=%" NSPRIu64 " op=%d EXT oid=\"%s\" name=\"%s\"\n", pb->pb_conn->c_connid, pb->pb_op->o_opid, extoid, name ); } /* during a bulk import, only BULK_IMPORT_DONE is allowed! * (and this is the only time it's allowed) */ if (pb->pb_conn->c_flags & CONN_FLAG_IMPORT) { if (strcmp(extoid, EXTOP_BULK_IMPORT_DONE_OID) != 0) { send_ldap_result(pb, LDAP_PROTOCOL_ERROR, NULL, NULL, 0, NULL); goto free_and_return; } extop_handle_import_done(pb, extoid, &extval); goto free_and_return; } if (strcmp(extoid, EXTOP_BULK_IMPORT_START_OID) == 0) { extop_handle_import_start(pb, extoid, &extval); goto free_and_return; } if (strcmp(extoid, START_TLS_OID) != 0) { int minssf = config_get_minssf(); /* If anonymous access is disabled and we haven't * authenticated yet, only allow startTLS. */ if ((config_get_anon_access_switch() != SLAPD_ANON_ACCESS_ON) && ((pb->pb_op->o_authtype == NULL) || (strcasecmp(pb->pb_op->o_authtype, SLAPD_AUTH_NONE) == 0))) { send_ldap_result( pb, LDAP_INAPPROPRIATE_AUTH, NULL, "Anonymous access is not allowed.", 0, NULL ); goto free_and_return; } /* If the minssf is not met, only allow startTLS. */ if ((pb->pb_conn->c_sasl_ssf < minssf) && (pb->pb_conn->c_ssl_ssf < minssf) && (pb->pb_conn->c_local_ssf < minssf)) { send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL, "Minimum SSF not met.", 0, NULL ); goto free_and_return; } } /* If a password change is required, only allow the password * modify extended operation */ if (!pb->pb_conn->c_isreplication_session && pb->pb_conn->c_needpw && (strcmp(extoid, EXTOP_PASSWD_OID) != 0)) { char *dn = NULL; slapi_pblock_get(pb, SLAPI_CONN_DN, &dn); (void)slapi_add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0); op_shared_log_error_access (pb, "EXT", dn ? dn : "", "need new password"); send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL, NULL, 0, NULL ); slapi_ch_free_string(&dn); goto free_and_return; } /* decode the optional controls - put them in the pblock */ if ( (lderr = get_ldapmessage_controls( pb, pb->pb_op->o_ber, NULL )) != 0 ) { char *dn = NULL; slapi_pblock_get(pb, SLAPI_CONN_DN, &dn); op_shared_log_error_access (pb, "EXT", dn ? dn : "", "failed to decode LDAP controls"); send_ldap_result( pb, lderr, NULL, NULL, 0, NULL ); slapi_ch_free_string(&dn); goto free_and_return; } slapi_pblock_set( pb, SLAPI_EXT_OP_REQ_OID, extoid ); slapi_pblock_set( pb, SLAPI_EXT_OP_REQ_VALUE, &extval ); slapi_pblock_set( pb, SLAPI_REQUESTOR_ISROOT, &pb->pb_op->o_isroot); rc = plugin_determine_exop_plugins( extoid, &p ); slapi_log_err(SLAPI_LOG_TRACE, "do_extended", "Plugin_determine_exop_plugins rc %d\n", rc); if (plugin_call_plugins(pb, SLAPI_PLUGIN_PRE_EXTOP_FN) != SLAPI_PLUGIN_SUCCESS) { goto free_and_return; } if (rc == SLAPI_PLUGIN_EXTENDEDOP && p != NULL) { slapi_log_err(SLAPI_LOG_TRACE, "do_extended", "Calling plugin ... \n"); /* * Return values: * SLAPI_PLUGIN_EXTENDED_SENT_RESULT: The result is already sent to the client. * There is nothing to do further. * SLAPI_PLUGIN_EXTENDED_NOT_HANDLED: Unsupported extended operation * LDAP codes (e.g., LDAP_SUCCESS): The result is not sent yet. Call send_ldap_result. */ rc = plugin_call_exop_plugins( pb, p); slapi_log_err(SLAPI_LOG_TRACE, "do_extended", "Called exop, got %d \n", rc); } else if (rc == SLAPI_PLUGIN_BETXNEXTENDEDOP && p != NULL) { slapi_log_err(SLAPI_LOG_TRACE, "do_extended", "Calling betxn plugin ... \n"); /* Look up the correct backend to use. */ Slapi_Backend *be = plugin_extended_op_getbackend( pb, p ); if ( be == NULL ) { slapi_log_err(SLAPI_LOG_ERR, "do_extended", "Plugin_extended_op_getbackend was unable to retrieve a backend!\n"); rc = LDAP_OPERATIONS_ERROR; } else { /* We need to make a new be pb here because when you set SLAPI_BACKEND * you overwrite the plg parts of the pb. So if we re-use pb * you actually nuke the request, and everything hangs. (╯°□°)╯︵ ┻━┻ */ Slapi_PBlock *be_pb = NULL; be_pb = slapi_pblock_new(); slapi_pblock_set(be_pb, SLAPI_BACKEND, be); int txn_rc = slapi_back_transaction_begin(be_pb); if (txn_rc) { slapi_log_err(SLAPI_LOG_ERR, "do_extended", "Failed to start be_txn for plugin_call_exop_plugins %d\n", txn_rc); } else { /* * Return values: * SLAPI_PLUGIN_EXTENDED_SENT_RESULT: The result is already sent to the client. * There is nothing to do further. * SLAPI_PLUGIN_EXTENDED_NOT_HANDLED: Unsupported extended operation * LDAP codes (e.g., LDAP_SUCCESS): The result is not sent yet. Call send_ldap_result. */ rc = plugin_call_exop_plugins( pb, p ); slapi_log_err(SLAPI_LOG_TRACE, "do_extended", "Called betxn exop, got %d \n", rc); if (rc == LDAP_SUCCESS || rc == SLAPI_PLUGIN_EXTENDED_SENT_RESULT) { /* commit */ txn_rc = slapi_back_transaction_commit(be_pb); if (txn_rc == 0) { slapi_log_err(SLAPI_LOG_TRACE, "do_extended", "Commit with result %d \n", txn_rc); } else { slapi_log_err(SLAPI_LOG_ERR, "do_extended", "Unable to commit commit with result %d \n", txn_rc); } } else { /* abort */ txn_rc = slapi_back_transaction_abort(be_pb); slapi_log_err(SLAPI_LOG_ERR, "do_extended", "Abort with result %d \n", txn_rc); } } /* txn_rc */ slapi_pblock_destroy(be_pb); /* Clean up after ourselves */ } /* if be */ } if (plugin_call_plugins(pb, SLAPI_PLUGIN_POST_EXTOP_FN) != SLAPI_PLUGIN_SUCCESS) { goto free_and_return; } if ( SLAPI_PLUGIN_EXTENDED_SENT_RESULT != rc ) { if ( SLAPI_PLUGIN_EXTENDED_NOT_HANDLED == rc ) { lderr = LDAP_PROTOCOL_ERROR; /* no plugin handled the op */ errmsg = "unsupported extended operation"; } else { if (rc != LDAP_SUCCESS) { slapi_log_err(SLAPI_LOG_ERR, "do_extended", "Failed with result %d \n", rc); } errmsg = NULL; lderr = rc; } send_ldap_result( pb, lderr, NULL, errmsg, 0, NULL ); } free_and_return: if (extoid) slapi_ch_free((void **)&extoid); if (extval.bv_val) slapi_ch_free((void **)&extval.bv_val); return; }
/* * Do the actual work of authenticating with PAM. First, get the PAM identity * based on the method used to convert the BIND identity to the PAM identity. * Set up the structures that pam_start needs and call pam_start(). After * that, call pam_authenticate and pam_acct_mgmt. Check the various return * values from these functions and map them to their corresponding LDAP BIND * return values. Return the appropriate LDAP error code. * This function will also set the appropriate LDAP response controls in * the given pblock. * Since this function can be called multiple times, we only want to return * the errors and controls to the user if this is the final call, so the * final_method parameter tells us if this is the last one. Coupled with * the fallback argument, we can tell if we are able to send the response * back to the client. */ static int do_one_pam_auth( Slapi_PBlock *pb, int method, /* get pam identity from ENTRY, RDN, or DN */ PRBool final_method, /* which method is the last one to try */ char *pam_service, /* name of service for pam_start() */ char *map_ident_attr, /* for ENTRY method, name of attribute holding pam identity */ PRBool fallback, /* if true, failure here should fallback to regular bind */ int pw_response_requested /* do we need to send pwd policy resp control */ ) { MyStrBuf pam_id; const char *binddn = NULL; Slapi_DN *bindsdn = NULL; int rc; int retcode = LDAP_SUCCESS; pam_handle_t *pam_handle; struct my_pam_conv_str my_data; struct pam_conv my_pam_conv = {pam_conv_func, NULL}; char *errmsg = NULL; /* free with PR_smprintf_free */ int locked = 0; slapi_pblock_get( pb, SLAPI_BIND_TARGET_SDN, &bindsdn ); if (NULL == bindsdn) { errmsg = PR_smprintf("Null bind dn"); retcode = LDAP_OPERATIONS_ERROR; pam_id.str = NULL; /* initialize pam_id.str */ goto done; /* skip the pam stuff */ } binddn = slapi_sdn_get_dn(bindsdn); if (method == PAMPT_MAP_METHOD_RDN) { derive_from_bind_dn(pb, bindsdn, &pam_id); } else if (method == PAMPT_MAP_METHOD_ENTRY) { derive_from_bind_entry(pb, bindsdn, &pam_id, map_ident_attr, &locked); } else { init_my_str_buf(&pam_id, binddn); } if (locked) { errmsg = PR_smprintf("Account inactivated. Contact system administrator."); retcode = LDAP_UNWILLING_TO_PERFORM; /* user inactivated */ goto done; /* skip the pam stuff */ } if (!pam_id.str) { errmsg = PR_smprintf("Bind DN [%s] is invalid or not found", binddn); retcode = LDAP_NO_SUCH_OBJECT; /* user unknown */ goto done; /* skip the pam stuff */ } /* do the pam stuff */ my_data.pb = pb; my_data.pam_identity = pam_id.str; my_pam_conv.appdata_ptr = &my_data; slapi_lock_mutex(PAMLock); /* from this point on we are in the critical section */ rc = pam_start(pam_service, pam_id.str, &my_pam_conv, &pam_handle); report_pam_error("during pam_start", rc, pam_handle); if (rc == PAM_SUCCESS) { /* use PAM_SILENT - there is no user interaction at this point */ rc = pam_authenticate(pam_handle, 0); report_pam_error("during pam_authenticate", rc, pam_handle); /* check different types of errors here */ if (rc == PAM_USER_UNKNOWN) { errmsg = PR_smprintf("User id [%s] for bind DN [%s] does not exist in PAM", pam_id.str, binddn); retcode = LDAP_NO_SUCH_OBJECT; /* user unknown */ } else if (rc == PAM_AUTH_ERR) { errmsg = PR_smprintf("Invalid PAM password for user id [%s], bind DN [%s]", pam_id.str, binddn); retcode = LDAP_INVALID_CREDENTIALS; /* invalid creds */ } else if (rc == PAM_MAXTRIES) { errmsg = PR_smprintf("Authentication retry limit exceeded in PAM for " "user id [%s], bind DN [%s]", pam_id.str, binddn); if (pw_response_requested) { slapi_pwpolicy_make_response_control(pb, -1, -1, LDAP_PWPOLICY_ACCTLOCKED); } retcode = LDAP_CONSTRAINT_VIOLATION; /* max retries */ } else if (rc != PAM_SUCCESS) { errmsg = PR_smprintf("Unknown PAM error [%s] for user id [%s], bind DN [%s]", pam_strerror(pam_handle, rc), pam_id.str, binddn); retcode = LDAP_OPERATIONS_ERROR; /* pam config or network problem */ } } /* if user authenticated successfully, see if there is anything we need to report back w.r.t. password or account lockout */ if (rc == PAM_SUCCESS) { rc = pam_acct_mgmt(pam_handle, 0); report_pam_error("during pam_acct_mgmt", rc, pam_handle); /* check different types of errors here */ if (rc == PAM_USER_UNKNOWN) { errmsg = PR_smprintf("User id [%s] for bind DN [%s] does not exist in PAM", pam_id.str, binddn); retcode = LDAP_NO_SUCH_OBJECT; /* user unknown */ } else if (rc == PAM_AUTH_ERR) { errmsg = PR_smprintf("Invalid PAM password for user id [%s], bind DN [%s]", pam_id.str, binddn); retcode = LDAP_INVALID_CREDENTIALS; /* invalid creds */ } else if (rc == PAM_PERM_DENIED) { errmsg = PR_smprintf("Access denied for PAM user id [%s], bind DN [%s]" " - see administrator", pam_id.str, binddn); if (pw_response_requested) { slapi_pwpolicy_make_response_control(pb, -1, -1, LDAP_PWPOLICY_ACCTLOCKED); } retcode = LDAP_UNWILLING_TO_PERFORM; } else if (rc == PAM_ACCT_EXPIRED) { errmsg = PR_smprintf("Expired PAM password for user id [%s], bind DN [%s]: " "reset required", pam_id.str, binddn); slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRED, 0); if (pw_response_requested) { slapi_pwpolicy_make_response_control(pb, -1, -1, LDAP_PWPOLICY_PWDEXPIRED); } retcode = LDAP_INVALID_CREDENTIALS; } else if (rc == PAM_NEW_AUTHTOK_REQD) { /* handled same way as ACCT_EXPIRED */ errmsg = PR_smprintf("Expired PAM password for user id [%s], bind DN [%s]: " "reset required", pam_id.str, binddn); slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRED, 0); if (pw_response_requested) { slapi_pwpolicy_make_response_control(pb, -1, -1, LDAP_PWPOLICY_PWDEXPIRED); } retcode = LDAP_INVALID_CREDENTIALS; } else if (rc != PAM_SUCCESS) { errmsg = PR_smprintf("Unknown PAM error [%s] for user id [%s], bind DN [%s]", pam_strerror(pam_handle, rc), pam_id.str, binddn); retcode = LDAP_OPERATIONS_ERROR; /* unknown */ } } rc = pam_end(pam_handle, rc); report_pam_error("during pam_end", rc, pam_handle); slapi_unlock_mutex(PAMLock); /* not in critical section any more */ done: delete_my_str_buf(&pam_id); if ((retcode == LDAP_SUCCESS) && (rc != PAM_SUCCESS)) { errmsg = PR_smprintf("Unknown PAM error [%d] for user id [%s], bind DN [%s]", rc, pam_id.str, binddn); retcode = LDAP_OPERATIONS_ERROR; } if (retcode != LDAP_SUCCESS) { slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM, "%s\n", errmsg); if (final_method && !fallback) { slapi_send_ldap_result(pb, retcode, NULL, errmsg, 0, NULL); } } if (errmsg) { PR_smprintf_free(errmsg); } return retcode; }