From 145db25c8e01d06efd534c75913dffec957ce3a6 Mon Sep 17 00:00:00 2001 From: Stefan Ritt Date: Fri, 23 Feb 2024 15:44:50 +0100 Subject: [PATCH] Changed Kerberos authentication to make password changed only affecting local password --- src/auth.cxx | 1280 +++++++++++++++++++++++++------------------------ src/elogd.cxx | 10 +- 2 files changed, 646 insertions(+), 644 deletions(-) diff --git a/src/auth.cxx b/src/auth.cxx index e0a47a48..9f19d88f 100644 --- a/src/auth.cxx +++ b/src/auth.cxx @@ -1,639 +1,641 @@ -/********************************************************************\ - - Name: auth.c - Created by: Stefan Ritt - Copyright 2000 + Stefan Ritt - - ELOG is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ELOG is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with ELOG. If not, see . - - - Contents: Authentication subroutines. Currently supported: - - - password file authentication - - kerberos5 password authentication - - $Id: elog.c 2350 2010-12-23 10:45:10Z ritt $ - -\********************************************************************/ - -#include "elogd.h" - -#ifdef HAVE_KRB5 -#include -#endif - -#ifdef HAVE_LDAP -#define LDAP_DEPRECATED 1 -#include - -LDAP *ldap_ld; -char ldap_login_attr[64]; -char ldap_userbase[256]; -char ldap_bindDN[512]; -#endif /* HAVE_LDAP */ - -#ifdef HAVE_PAM -#include -#endif /* HAVE_PAM */ - -extern LOGBOOK *lb_list; - -/*==================================================================*/ - -/*---- Kerberos5 routines ------------------------------------------*/ - -#ifdef HAVE_KRB5 - -int auth_verify_password_krb5(LOGBOOK * lbs, const char *user, const char *password, char *error_str, - int error_size) -{ - char *princ_name, str[256], realm[256]; - krb5_error_code error; - krb5_principal princ; - krb5_context context; - krb5_creds creds; - krb5_get_init_creds_opt options; - - if (krb5_init_context(&context) < 0) - return FALSE; - - strlcpy(str, user, sizeof(str)); - if (getcfg(lbs->name, "Kerberos Realm", realm, sizeof(realm))) { - strlcat(str, "@", sizeof(str)); - strlcat(str, realm, sizeof(str)); - } - if ((error = krb5_parse_name(context, str, &princ)) != 0) { - strlcpy(error_str, "Kerberos error:
", error_size); - strlcat(error_str, krb5_get_error_message(context, error), error_size); - strlcat(error_str, ".
Please check your Kerberos configuration.", error_size); - return FALSE; - } - - error = krb5_unparse_name(context, princ, &princ_name); - if (error) { - strlcpy(error_str, "Kerberos error:
", error_size); - strlcat(error_str, krb5_get_error_message(context, error), error_size); - strlcat(error_str, ".
Please check your Kerberos configuration.", error_size); - return FALSE; - } - - sprintf(str, "Using %s as server principal for authentication", princ_name); - write_logfile(lbs, str); - - memset(&options, 0, sizeof(options)); - krb5_get_init_creds_opt_init(&options); - memset(&creds, 0, sizeof(creds)); - error = krb5_get_init_creds_password(context, &creds, princ, - (char *) password, NULL, NULL, 0, NULL, &options); - - krb5_free_context(context); - - if (error && error != KRB5KDC_ERR_PREAUTH_FAILED && error != KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN) { - sprintf(error_str, "Kerberos error %d:
", error); - strlcat(error_str, krb5_get_error_message(context, error), error_size); - strlcat(error_str, ".
Please check your Kerberos configuration.", error_size); - return FALSE; - } - - if (error) - return FALSE; - - return TRUE; -} - -int auth_change_password_krb5(LOGBOOK * lbs, const char *user, const char *old_pwd, const char *new_pwd, - char *error_str, int error_size) -{ - char *princ_name, str[256], realm[256]; - int result_code, n; - krb5_error_code error; - krb5_data result_code_string, result_string; - krb5_principal princ; - krb5_context context; - krb5_creds creds; - krb5_get_init_creds_opt options; - - if (krb5_init_context(&context) < 0) - return FALSE; - - strlcpy(str, user, sizeof(str)); - if (getcfg(lbs->name, "Kerberos Realm", realm, sizeof(realm))) { - strlcat(str, "@", sizeof(str)); - strlcat(str, realm, sizeof(str)); - } - if ((error = krb5_parse_name(context, str, &princ)) != 0) { - strlcpy(error_str, "Kerberos error:
", error_size); - strlcat(error_str, krb5_get_error_message(context, error), error_size); - strlcat(error_str, ".
Please check your Kerberos configuration.", error_size); - return FALSE; - } - - error = krb5_unparse_name(context, princ, &princ_name); - - sprintf(str, "Using %s as server principal for authentication", princ_name); - write_logfile(lbs, str); - - memset(&options, 0, sizeof(options)); - krb5_get_init_creds_opt_init(&options); - krb5_get_init_creds_opt_set_tkt_life(&options, 300); - krb5_get_init_creds_opt_set_forwardable(&options, FALSE); - krb5_get_init_creds_opt_set_proxiable(&options, FALSE); - - memset(&creds, 0, sizeof(creds)); - error = krb5_get_init_creds_password(context, &creds, princ, - (char *) old_pwd, NULL, NULL, 0, "kadmin/changepw", &options); - if (error) { - strlcpy(error_str, "Kerberos error:
", error_size); - strlcat(error_str, krb5_get_error_message(context, error), error_size); - strlcat(error_str, ".
Please check your Kerberos configuration.", error_size); - return FALSE; - } - - error = krb5_set_password(context, &creds, (char *) new_pwd, princ, - &result_code, &result_code_string, &result_string); - if (error) { - strlcpy(error_str, "Kerberos error:
", error_size); - strlcat(error_str, krb5_get_error_message(context, error), error_size); - strlcat(error_str, ".
Please check your Kerberos configuration.", error_size); - return FALSE; - } - - if (result_code > 0) { - if (result_code_string.length > 0) { - strlcpy(error_str, result_code_string.data, error_size); - if ((int) result_code_string.length < error_size) - error_str[result_code_string.length] = 0; - } - if (result_string.length > 0) { - strlcat(error_str, ": ", error_size); - n = strlen(error_str) + result_string.length; - strlcat(error_str, result_string.data, error_size); - if (n < error_size) - error_str[n] = 0; - } - } - - krb5_free_data_contents(context, &result_code_string); - krb5_free_data_contents(context, &result_string); - krb5_free_cred_contents(context, &creds); - krb5_get_init_creds_opt_free(context, &options); - krb5_free_context(context); - - if (result_code > 0) - return FALSE; - - return TRUE; -} -#endif - -/*---- LDAP routines ------------------------------------------*/ - -#ifdef HAVE_LDAP - -int ldap_init(LOGBOOK *lbs, char *error_str, int error_size) -{ - char str[512], ldap_server[256]; - int ii, version; - - // Read Config file - if (getcfg(lbs->name, "LDAP server", ldap_server, sizeof(ldap_server))) { - strlcpy(str, ldap_server, sizeof(str)); - } - else { - strlcpy(error_str, "LDAP initialization error
", error_size); - strlcat(error_str, "
Please check your LDAP configuration.", error_size); - strlcat(str, "ERR: Cannot find LDAP server entry!", sizeof(str)); - write_logfile(lbs, str); - return FALSE; - } - - if (!getcfg(lbs->name, "LDAP userbase", ldap_userbase, sizeof(ldap_userbase))) { - strlcpy(error_str, "LDAP initialization error
", error_size); - strlcat(error_str, "
Please check your LDAP configuration.", error_size); - strlcat(str, ", ERR: Cannot find LDAP userbase (e.g. \'ou=People,dc=example,dc=org\')!", sizeof(str)); - write_logfile(lbs, str); - return FALSE; - } - - if (!getcfg(lbs->name, "LDAP login attribute", ldap_login_attr, sizeof(ldap_login_attr))) { - strlcpy(error_str, "LDAP initialization error
", error_size); - strlcat(error_str, "
Please check your LDAP configuration.", error_size); - strlcat(str, ", ERR: Cannot find LDAP login attribute (e.g. uid, cn, ...)!", sizeof(str)); - write_logfile(lbs, str); - return FALSE; - } - - // Initialize/open LDAP connection - if(ldap_initialize( &ldap_ld, ldap_server )) { - perror("ldap_initialize"); - strlcpy(error_str, "LDAP initialization error
", error_size); - strlcat(error_str, "
Please check your LDAP configuration.", error_size); - return FALSE; - } - - // Use the LDAP_OPT_PROTOCOL_VERSION session preference to specify that the client is LDAPv3 client - version = LDAP_VERSION3; - ldap_set_option(ldap_ld, LDAP_OPT_PROTOCOL_VERSION, &version); - - write_logfile(lbs, str); - - return TRUE; -} - -int auth_verify_password_ldap(LOGBOOK *lbs, const char *user, const char *password, char *error_str, - int error_size) -{ LDAPMessage *result, *err; - int bind=0, ii; - char str[512]; - - ldap_ld = NULL; - memset(&ldap_bindDN[0], 0, sizeof(ldap_bindDN)); - - if (!strcmp(password, "")) { - strlcpy(error_str, "LDAP authentication failed, no password provided", error_size); - write_logfile(lbs, "LDAP authentication failed, no password provided"); - return FALSE; - } - - if (!ldap_init(lbs,error_str, error_size)) { - strlcpy(error_str, "LDAP initialization error
", error_size); - strlcat(error_str, "
Please check your LDAP configuration.", error_size); - return FALSE; - } - - // Form LDAP bind DN (distinguished name): - // login_attr=user,ldap_userbase, e.g. uid=tuser,ou=People,dc=example,dc=org - sprintf(ldap_bindDN,"%s=%s,%s",ldap_login_attr,user,ldap_userbase); - - strlcat(str, "Connecting as: ", sizeof(str)); - strlcat(str, ldap_bindDN, sizeof(str)); - write_logfile(lbs, str); - - // User authentication (bind) - bind = ldap_simple_bind_s(ldap_ld, ldap_bindDN, password); - if( bind != LDAP_SUCCESS ) { - strlcpy(error_str, "LDAP authentication error:
", error_size); - strlcat(error_str, ldap_err2string(bind), error_size); - strlcat(error_str, ".
Please check your user/password or LDAP configuration.", error_size); - strlcpy(str, "LDAP Authentication: FAILED!", sizeof(str)); - write_logfile(lbs, str); - ldap_unbind(ldap_ld); - return FALSE; - } - - strlcpy(str, "LDAP Authentication: Success!", sizeof(str)); - ldap_unbind(ldap_ld); - - write_logfile(lbs, str); - return TRUE; -} - - -int ldap_adduser_file(LOGBOOK *lbs, const char *user, const char *password, char *error_str, - int error_size) -{ LDAPMessage *result, *entry; - char *attribute, **values; - char str[512], filter[512]; - BerElement *ber; - int bind=0, rc=0, i; - - char lbs_str[256], user_str[256], user_enc[256], fullname[256], usergn[128], usersn[128], useremail[256]; - PMXML_NODE node, npwd; - - struct timeval timeOut = {3,0}; // 3 second connection/search timeout - // zerotime.tv_sec = zerotime.tv_usec = 0L; - - write_logfile(lbs, "New user: getting userdata from LDAP..."); - - if(!ldap_init(lbs,error_str,error_size)) { - return FALSE; - } - - // User authentication (bind) - bind = ldap_simple_bind_s(ldap_ld, ldap_bindDN, password); - if( bind != LDAP_SUCCESS ) { - strlcpy(error_str, "LDAP authentication error:
", error_size); - strlcat(error_str, ldap_err2string(bind), error_size); - strlcat(error_str, ".
Please check your user/password or LDAP configuration.", error_size); - strlcat(str, "LDAP Authentication: FAILED!", sizeof(str)); - write_logfile(lbs, str); - ldap_unbind(ldap_ld); - return FALSE; - } - - // form LDAP filter to find the user; - sprintf(filter, "(%s=%s)", ldap_login_attr, user); - - // below based on: http://www.djack.com.pl/modules.php?name=FAQ&myfaq=yes&xmyfaq=yes&id_cat=7&id=183 (code errors!) - // AND on: http://www-archive.mozilla.org/directory/csdk-docs/example.htm - - // Get user's first name, surname and email from LDAP - rc = ldap_search_ext_s( - ldap_ld, // LDAP session handle - ldap_userbase, // Search Base - LDAP_SCOPE_SUBTREE, // Search Scope – everything below o=Acme - filter, // Search Filter – only inetOrgPerson objects - NULL, // returnAllAttributes – NULL means Yes - 0, // attributesOnly – False means we want values - NULL, // Server controls – There are none - NULL, // Client controls – There are none - &timeOut, // search Timeout - LDAP_NO_LIMIT, // no size limit - &result); - - if (rc != LDAP_SUCCESS) { - strlcat(str, "LDAP search returned error: ", sizeof(str)); - strlcat(str, ldap_err2string(rc), sizeof(str)); - write_logfile(lbs, str); - ldap_unbind(ldap_ld); - return FALSE; - } - - for(entry = ldap_first_entry(ldap_ld,result); - entry != NULL; - entry = ldap_next_entry(ldap_ld,entry) ) { - for(attribute = ldap_first_attribute(ldap_ld,entry,&ber); - attribute != NULL; - attribute = ldap_next_attribute(ldap_ld,entry,ber) ) { - // For each attribute, print the attribute name and values. // - if((values = ldap_get_values(ldap_ld,entry,attribute)) != NULL ) { - for(i=0; values[i] != NULL; i++) { - if(strcmp(attribute,"givenName")==0 || strcmp(attribute,"gn")==0) - strlcpy(usergn, values[i], sizeof(usergn)); - if(strcmp(attribute,"sn")==0 || strcmp(attribute,"surname")==0) - strlcpy(usersn, values[i], sizeof(usersn)); - if(strcmp(attribute,"mail")==0 || strcmp(attribute,"rfc822Mailbox")==0) - strlcpy(useremail, values[i], sizeof(useremail)); - } - ldap_value_free(values); - } - ldap_memfree(attribute); - } - if(ber != NULL) ber_free(ber,0); - } - - ldap_msgfree(result); - ldap_unbind(ldap_ld); - - sprintf(fullname, "%s %s", usergn, usersn); - - // Add user from LDAP in the local password file - // do not allow HTML in user name - strencode2(user_enc, user, sizeof(user_enc)); - - sprintf(lbs_str, "/list/user[name=%s]", user_enc); - node = mxml_find_node(lbs->pwd_xml_tree, lbs_str); - node = mxml_find_node(lbs->pwd_xml_tree, "/list"); - if (!node) { - show_error(loc("Error accessing password file")); - return 0; - } - node = mxml_add_node(node, "user", NULL); - mxml_add_node(node, "name", user_enc); // add user login from LDAP; - - do_crypt(password, user_str, sizeof(str)); - npwd = mxml_add_node(node, "password", user_str); // add user password; - - if (npwd) mxml_add_attribute(npwd, "encoding", "SHA256"); // user password is encoded; - - strencode2(user_str, fullname, sizeof(str)); // add full user name from LDAP; - mxml_add_node(node, "full_name", user_str); - - mxml_add_node(node, "last_logout", "0"); // last logout; - mxml_add_node(node, "last_activity", "0"); // last activity; - - mxml_add_node(node, "email", useremail); // add user email from LDAP; - mxml_add_node(node, "inactive", "0"); - - sprintf(str,"New user: %s, %s added", user_enc, useremail); - write_logfile(lbs, str); - return TRUE; -} - -/*---- clear ldap_ld and ldap_bindDN ----*/ -int ldap_clear () -{ - ldap_ld = NULL; - memset(&ldap_bindDN[0], 0, sizeof(ldap_bindDN)); - - return TRUE; -} - -#endif /* LDAP */ - -/* PAM authentication routines */ - -#ifdef HAVE_PAM -/* we need a custom PAM conversation function to handle acquiring the auth - * token (password) from the web formular, and hand it to PAM */ -int elog_conv(int num_msg, const struct pam_message **mess, struct pam_response **resp, void *my_data) -{ - char *resptok; - - /* no PAM message received, this is an error */ - if(num_msg <= 0 || num_msg >= PAM_MAX_NUM_MSG) { - *resp = NULL; - return (PAM_CONV_ERR); - } - - /* if we do not have enough space to allocate the response, we have an error - * */ - if((*resp = (pam_response*) calloc(num_msg, sizeof(struct pam_response))) == NULL) - return (PAM_BUF_ERR); - - /* this is the password we got through the UI, this is put into the - * response, and given to pam_authenticate */ - if(!(resptok = strdup((const char *)my_data))) { - free(resp); - return (PAM_BUF_ERR); - } - - /* set the response to our auth token (password) */ - (*resp)->resp = resptok; - - return (PAM_SUCCESS); -} - -int auth_verify_password_pam(LOGBOOK *lbs, const char *user, const char *password, char *error_str, int error_size) -{ - pam_handle_t *pamh; - int retval; - char str[256]; - - int verified = 0; - /* use our custom conversation function */ - static struct pam_conv elog_pam_conv = { - elog_conv, - NULL - }; - - /* set conversation application data to our password */ - elog_pam_conv.appdata_ptr = (char *)password; - - /* start PAM auth procedure */ - sprintf(str, "[PAM] Starting authentication for user %s", user); - write_logfile(lbs, str); - retval = pam_start("elogd", user, &elog_pam_conv, &pamh); - - /* if we can use PAM, try to authenticate using our conversation method */ - if(retval == PAM_SUCCESS) { - retval = pam_authenticate(pamh, 0); - } - - /* if the user authenticated, see if the acc is valid */ - if(retval == PAM_SUCCESS) { - retval = pam_acct_mgmt(pamh, 0); - } - - verified = (retval == PAM_SUCCESS); - - if(verified) - sprintf(str, "[PAM] Authentication successful for user %s", user); - else - sprintf(str, "[PAM] Authentication not successful for user %s", user); - write_logfile(lbs, str); - - if(pam_end(pamh, retval) != PAM_SUCCESS) { - pamh = NULL; - strlcpy(error_str, "PAM: Error releasing authenticator", error_size); - } - - return verified; -} -#endif /* HAVE_PAM */ - -/*---- local password file routines --------------------------------*/ - -int auth_verify_password_file(LOGBOOK * lbs, const char *user, const char *password, char *error_str, - int error_size) -{ - char upwd[256], enc_pwd[256]; - - if (error_size) - *error_str = 0; - - get_user_line(lbs, (char *) user, upwd, NULL, NULL, NULL, NULL, NULL); - do_crypt(password, enc_pwd, sizeof(enc_pwd)); - - return strcmp(enc_pwd, upwd) == 0; -} - -int auth_change_password_file(LOGBOOK * lbs, const char *user, const char *new_pwd, - char *error_str, int error_size) -{ - char str[256], file_name[256], enc_pwd[256]; - PMXML_NODE node; - - if (error_size) - *error_str = 0; - - if (lbs == NULL) - lbs = get_first_lbs_with_global_passwd(); - - if (!lbs->pwd_xml_tree) - return FALSE; - - sprintf(str, "/list/user[name=%s]/password", user); - node = mxml_find_node(lbs->pwd_xml_tree, str); - if (node == NULL) - return FALSE; - - do_crypt(new_pwd, enc_pwd, sizeof(enc_pwd)); - mxml_replace_node_value(node, enc_pwd); - - if (get_password_file(lbs, file_name, sizeof(file_name))) - mxml_write_tree(file_name, lbs->pwd_xml_tree); - - return TRUE; -} - -/*---- common function entry points --------------------------------*/ - -int auth_verify_password(LOGBOOK * lbs, const char *user, const char *password, char *error_str, - int error_size) -{ - char str[256]; - BOOL verified; - - error_str[0] = 0; - verified = FALSE; - - /* otherwise calls with null lbs which make this procedure crash */ - if (lbs == NULL) - lbs = get_first_lbs_with_global_passwd(); - - if (lbs == NULL) - return FALSE; - getcfg(lbs->name, "Authentication", str, sizeof(str)); - -#ifdef HAVE_KRB5 - if (stristr(str, "Kerberos")) - verified = auth_verify_password_krb5(lbs, user, password, error_str, error_size); - if (verified) - return TRUE; -#endif - -#ifdef HAVE_LDAP - if (stristr(str, "LDAP")) { - verified = auth_verify_password_ldap(lbs, user, password, error_str, error_size); - - // if user not in password file (external authentication!) and "LDAP register" is allowed (>0), - // obtain user info from LDAP and add locally - if (verified) { - if (get_user_line(lbs, (char *)user, NULL, NULL, NULL, NULL, NULL, NULL) == 2) { - if (getcfg(lbs->name, "LDAP register", str, sizeof(str)) && atoi(str) > 0) - ldap_adduser_file(lbs, user, password, error_str, error_size); - } - } - - ldap_clear(); - } - if (verified) - return TRUE; -#endif - -#ifdef HAVE_PAM - if(stristr(str, "PAM")) - verified = auth_verify_password_pam(lbs, user, password, error_str, error_size); - if(verified) - return TRUE; -#endif /* HAVE_PAM */ - - if (str[0] == 0 || stristr(str, "File")) - verified = auth_verify_password_file(lbs, user, password, error_str, error_size); - - return verified; -} - -int auth_change_password(LOGBOOK * lbs, const char *user, const char *old_pwd, const char *new_pwd, - char *error_str, int error_size) -{ - int status = 0; - char str[256]; - - error_str[0] = 0; - getcfg(lbs->name, "Authentication", str, sizeof(str)); - - if (str[0] == 0 || stristr(str, "File")) - status = auth_change_password_file(lbs, user, new_pwd, error_str, error_size); - - if (old_pwd) {} // avoid compiler warning - -#ifdef HAVE_KRB5 - if (stristr(str, "Kerberos")) - status = auth_change_password_krb5(lbs, user, old_pwd, new_pwd, error_str, error_size); -#endif - - return status; -} +/********************************************************************\ + + Name: auth.c + Created by: Stefan Ritt + Copyright 2000 + Stefan Ritt + + ELOG is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ELOG is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ELOG. If not, see . + + + Contents: Authentication subroutines. Currently supported: + + - password file authentication + - kerberos5 password authentication + + $Id: elog.c 2350 2010-12-23 10:45:10Z ritt $ + +\********************************************************************/ + +#include "elogd.h" + +#ifdef HAVE_KRB5 +#include +#endif + +#ifdef HAVE_LDAP +#define LDAP_DEPRECATED 1 +#include + +LDAP *ldap_ld; +char ldap_login_attr[64]; +char ldap_userbase[256]; +char ldap_bindDN[512]; +#endif /* HAVE_LDAP */ + +#ifdef HAVE_PAM +#include +#endif /* HAVE_PAM */ + +extern LOGBOOK *lb_list; + +/*==================================================================*/ + +/*---- Kerberos5 routines ------------------------------------------*/ + +#ifdef HAVE_KRB5 + +int auth_verify_password_krb5(LOGBOOK * lbs, const char *user, const char *password, char *error_str, + int error_size) +{ + char *princ_name, str[256], realm[256]; + krb5_error_code error; + krb5_principal princ; + krb5_context context; + krb5_creds creds; + krb5_get_init_creds_opt options; + + if (krb5_init_context(&context) < 0) + return FALSE; + + strlcpy(str, user, sizeof(str)); + if (getcfg(lbs->name, "Kerberos Realm", realm, sizeof(realm))) { + strlcat(str, "@", sizeof(str)); + strlcat(str, realm, sizeof(str)); + } + if ((error = krb5_parse_name(context, str, &princ)) != 0) { + strlcpy(error_str, "Kerberos error:
", error_size); + strlcat(error_str, krb5_get_error_message(context, error), error_size); + strlcat(error_str, ".
Please check your Kerberos configuration.", error_size); + return FALSE; + } + + error = krb5_unparse_name(context, princ, &princ_name); + if (error) { + strlcpy(error_str, "Kerberos error:
", error_size); + strlcat(error_str, krb5_get_error_message(context, error), error_size); + strlcat(error_str, ".
Please check your Kerberos configuration.", error_size); + return FALSE; + } + + sprintf(str, "Using %s as server principal for authentication", princ_name); + write_logfile(lbs, str); + + memset(&options, 0, sizeof(options)); + krb5_get_init_creds_opt_init(&options); + memset(&creds, 0, sizeof(creds)); + error = krb5_get_init_creds_password(context, &creds, princ, + (char *) password, NULL, NULL, 0, NULL, &options); + + krb5_free_context(context); + + if (error && error != KRB5KDC_ERR_PREAUTH_FAILED && error != KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN) { + sprintf(error_str, "Kerberos error %d:
", error); + strlcat(error_str, krb5_get_error_message(context, error), error_size); + strlcat(error_str, ".
Please check your Kerberos configuration.", error_size); + return FALSE; + } + + if (error) + return FALSE; + + return TRUE; +} + +int auth_change_password_krb5(LOGBOOK * lbs, const char *user, const char *old_pwd, const char *new_pwd, + char *error_str, int error_size) +{ + char *princ_name, str[256], realm[256]; + int result_code, n; + krb5_error_code error; + krb5_data result_code_string, result_string; + krb5_principal princ; + krb5_context context; + krb5_creds creds; + krb5_get_init_creds_opt options; + + if (krb5_init_context(&context) < 0) + return FALSE; + + strlcpy(str, user, sizeof(str)); + if (getcfg(lbs->name, "Kerberos Realm", realm, sizeof(realm))) { + strlcat(str, "@", sizeof(str)); + strlcat(str, realm, sizeof(str)); + } + if ((error = krb5_parse_name(context, str, &princ)) != 0) { + strlcpy(error_str, "Kerberos error:
", error_size); + strlcat(error_str, krb5_get_error_message(context, error), error_size); + strlcat(error_str, ".
Please check your Kerberos configuration.", error_size); + return FALSE; + } + + error = krb5_unparse_name(context, princ, &princ_name); + + sprintf(str, "Using %s as server principal for authentication", princ_name); + write_logfile(lbs, str); + + memset(&options, 0, sizeof(options)); + krb5_get_init_creds_opt_init(&options); + krb5_get_init_creds_opt_set_tkt_life(&options, 300); + krb5_get_init_creds_opt_set_forwardable(&options, FALSE); + krb5_get_init_creds_opt_set_proxiable(&options, FALSE); + + memset(&creds, 0, sizeof(creds)); + error = krb5_get_init_creds_password(context, &creds, princ, + (char *) old_pwd, NULL, NULL, 0, "kadmin/changepw", &options); + if (error) { + strlcpy(error_str, "Kerberos error:
", error_size); + strlcat(error_str, krb5_get_error_message(context, error), error_size); + strlcat(error_str, ".
Please check your Kerberos configuration.", error_size); + return FALSE; + } + + error = krb5_set_password(context, &creds, (char *) new_pwd, princ, + &result_code, &result_code_string, &result_string); + if (error) { + strlcpy(error_str, "Kerberos error:
", error_size); + strlcat(error_str, krb5_get_error_message(context, error), error_size); + strlcat(error_str, ".
Please check your Kerberos configuration.", error_size); + return FALSE; + } + + if (result_code > 0) { + if (result_code_string.length > 0) { + strlcpy(error_str, result_code_string.data, error_size); + if ((int) result_code_string.length < error_size) + error_str[result_code_string.length] = 0; + } + if (result_string.length > 0) { + strlcat(error_str, ": ", error_size); + n = strlen(error_str) + result_string.length; + strlcat(error_str, result_string.data, error_size); + if (n < error_size) + error_str[n] = 0; + } + } + + krb5_free_data_contents(context, &result_code_string); + krb5_free_data_contents(context, &result_string); + krb5_free_cred_contents(context, &creds); + krb5_get_init_creds_opt_free(context, &options); + krb5_free_context(context); + + if (result_code > 0) + return FALSE; + + return TRUE; +} +#endif + +/*---- LDAP routines ------------------------------------------*/ + +#ifdef HAVE_LDAP + +int ldap_init(LOGBOOK *lbs, char *error_str, int error_size) +{ + char str[512], ldap_server[256]; + int ii, version; + + // Read Config file + if (getcfg(lbs->name, "LDAP server", ldap_server, sizeof(ldap_server))) { + strlcpy(str, ldap_server, sizeof(str)); + } + else { + strlcpy(error_str, "LDAP initialization error
", error_size); + strlcat(error_str, "
Please check your LDAP configuration.", error_size); + strlcat(str, "ERR: Cannot find LDAP server entry!", sizeof(str)); + write_logfile(lbs, str); + return FALSE; + } + + if (!getcfg(lbs->name, "LDAP userbase", ldap_userbase, sizeof(ldap_userbase))) { + strlcpy(error_str, "LDAP initialization error
", error_size); + strlcat(error_str, "
Please check your LDAP configuration.", error_size); + strlcat(str, ", ERR: Cannot find LDAP userbase (e.g. \'ou=People,dc=example,dc=org\')!", sizeof(str)); + write_logfile(lbs, str); + return FALSE; + } + + if (!getcfg(lbs->name, "LDAP login attribute", ldap_login_attr, sizeof(ldap_login_attr))) { + strlcpy(error_str, "LDAP initialization error
", error_size); + strlcat(error_str, "
Please check your LDAP configuration.", error_size); + strlcat(str, ", ERR: Cannot find LDAP login attribute (e.g. uid, cn, ...)!", sizeof(str)); + write_logfile(lbs, str); + return FALSE; + } + + // Initialize/open LDAP connection + if(ldap_initialize( &ldap_ld, ldap_server )) { + perror("ldap_initialize"); + strlcpy(error_str, "LDAP initialization error
", error_size); + strlcat(error_str, "
Please check your LDAP configuration.", error_size); + return FALSE; + } + + // Use the LDAP_OPT_PROTOCOL_VERSION session preference to specify that the client is LDAPv3 client + version = LDAP_VERSION3; + ldap_set_option(ldap_ld, LDAP_OPT_PROTOCOL_VERSION, &version); + + write_logfile(lbs, str); + + return TRUE; +} + +int auth_verify_password_ldap(LOGBOOK *lbs, const char *user, const char *password, char *error_str, + int error_size) +{ LDAPMessage *result, *err; + int bind=0, ii; + char str[512]; + + ldap_ld = NULL; + memset(&ldap_bindDN[0], 0, sizeof(ldap_bindDN)); + + if (!strcmp(password, "")) { + strlcpy(error_str, "LDAP authentication failed, no password provided", error_size); + write_logfile(lbs, "LDAP authentication failed, no password provided"); + return FALSE; + } + + if (!ldap_init(lbs,error_str, error_size)) { + strlcpy(error_str, "LDAP initialization error
", error_size); + strlcat(error_str, "
Please check your LDAP configuration.", error_size); + return FALSE; + } + + // Form LDAP bind DN (distinguished name): + // login_attr=user,ldap_userbase, e.g. uid=tuser,ou=People,dc=example,dc=org + sprintf(ldap_bindDN,"%s=%s,%s",ldap_login_attr,user,ldap_userbase); + + strlcat(str, "Connecting as: ", sizeof(str)); + strlcat(str, ldap_bindDN, sizeof(str)); + write_logfile(lbs, str); + + // User authentication (bind) + bind = ldap_simple_bind_s(ldap_ld, ldap_bindDN, password); + if( bind != LDAP_SUCCESS ) { + strlcpy(error_str, "LDAP authentication error:
", error_size); + strlcat(error_str, ldap_err2string(bind), error_size); + strlcat(error_str, ".
Please check your user/password or LDAP configuration.", error_size); + strlcpy(str, "LDAP Authentication: FAILED!", sizeof(str)); + write_logfile(lbs, str); + ldap_unbind(ldap_ld); + return FALSE; + } + + strlcpy(str, "LDAP Authentication: Success!", sizeof(str)); + ldap_unbind(ldap_ld); + + write_logfile(lbs, str); + return TRUE; +} + + +int ldap_adduser_file(LOGBOOK *lbs, const char *user, const char *password, char *error_str, + int error_size) +{ LDAPMessage *result, *entry; + char *attribute, **values; + char str[512], filter[512]; + BerElement *ber; + int bind=0, rc=0, i; + + char lbs_str[256], user_str[256], user_enc[256], fullname[256], usergn[128], usersn[128], useremail[256]; + PMXML_NODE node, npwd; + + struct timeval timeOut = {3,0}; // 3 second connection/search timeout + // zerotime.tv_sec = zerotime.tv_usec = 0L; + + write_logfile(lbs, "New user: getting userdata from LDAP..."); + + if(!ldap_init(lbs,error_str,error_size)) { + return FALSE; + } + + // User authentication (bind) + bind = ldap_simple_bind_s(ldap_ld, ldap_bindDN, password); + if( bind != LDAP_SUCCESS ) { + strlcpy(error_str, "LDAP authentication error:
", error_size); + strlcat(error_str, ldap_err2string(bind), error_size); + strlcat(error_str, ".
Please check your user/password or LDAP configuration.", error_size); + strlcat(str, "LDAP Authentication: FAILED!", sizeof(str)); + write_logfile(lbs, str); + ldap_unbind(ldap_ld); + return FALSE; + } + + // form LDAP filter to find the user; + sprintf(filter, "(%s=%s)", ldap_login_attr, user); + + // below based on: http://www.djack.com.pl/modules.php?name=FAQ&myfaq=yes&xmyfaq=yes&id_cat=7&id=183 (code errors!) + // AND on: http://www-archive.mozilla.org/directory/csdk-docs/example.htm + + // Get user's first name, surname and email from LDAP + rc = ldap_search_ext_s( + ldap_ld, // LDAP session handle + ldap_userbase, // Search Base + LDAP_SCOPE_SUBTREE, // Search Scope – everything below o=Acme + filter, // Search Filter – only inetOrgPerson objects + NULL, // returnAllAttributes – NULL means Yes + 0, // attributesOnly – False means we want values + NULL, // Server controls – There are none + NULL, // Client controls – There are none + &timeOut, // search Timeout + LDAP_NO_LIMIT, // no size limit + &result); + + if (rc != LDAP_SUCCESS) { + strlcat(str, "LDAP search returned error: ", sizeof(str)); + strlcat(str, ldap_err2string(rc), sizeof(str)); + write_logfile(lbs, str); + ldap_unbind(ldap_ld); + return FALSE; + } + + for(entry = ldap_first_entry(ldap_ld,result); + entry != NULL; + entry = ldap_next_entry(ldap_ld,entry) ) { + for(attribute = ldap_first_attribute(ldap_ld,entry,&ber); + attribute != NULL; + attribute = ldap_next_attribute(ldap_ld,entry,ber) ) { + // For each attribute, print the attribute name and values. // + if((values = ldap_get_values(ldap_ld,entry,attribute)) != NULL ) { + for(i=0; values[i] != NULL; i++) { + if(strcmp(attribute,"givenName")==0 || strcmp(attribute,"gn")==0) + strlcpy(usergn, values[i], sizeof(usergn)); + if(strcmp(attribute,"sn")==0 || strcmp(attribute,"surname")==0) + strlcpy(usersn, values[i], sizeof(usersn)); + if(strcmp(attribute,"mail")==0 || strcmp(attribute,"rfc822Mailbox")==0) + strlcpy(useremail, values[i], sizeof(useremail)); + } + ldap_value_free(values); + } + ldap_memfree(attribute); + } + if(ber != NULL) ber_free(ber,0); + } + + ldap_msgfree(result); + ldap_unbind(ldap_ld); + + sprintf(fullname, "%s %s", usergn, usersn); + + // Add user from LDAP in the local password file + // do not allow HTML in user name + strencode2(user_enc, user, sizeof(user_enc)); + + sprintf(lbs_str, "/list/user[name=%s]", user_enc); + node = mxml_find_node(lbs->pwd_xml_tree, lbs_str); + node = mxml_find_node(lbs->pwd_xml_tree, "/list"); + if (!node) { + show_error(loc("Error accessing password file")); + return 0; + } + node = mxml_add_node(node, "user", NULL); + mxml_add_node(node, "name", user_enc); // add user login from LDAP; + + do_crypt(password, user_str, sizeof(str)); + npwd = mxml_add_node(node, "password", user_str); // add user password; + + if (npwd) mxml_add_attribute(npwd, "encoding", "SHA256"); // user password is encoded; + + strencode2(user_str, fullname, sizeof(str)); // add full user name from LDAP; + mxml_add_node(node, "full_name", user_str); + + mxml_add_node(node, "last_logout", "0"); // last logout; + mxml_add_node(node, "last_activity", "0"); // last activity; + + mxml_add_node(node, "email", useremail); // add user email from LDAP; + mxml_add_node(node, "inactive", "0"); + + sprintf(str,"New user: %s, %s added", user_enc, useremail); + write_logfile(lbs, str); + return TRUE; +} + +/*---- clear ldap_ld and ldap_bindDN ----*/ +int ldap_clear () +{ + ldap_ld = NULL; + memset(&ldap_bindDN[0], 0, sizeof(ldap_bindDN)); + + return TRUE; +} + +#endif /* LDAP */ + +/* PAM authentication routines */ + +#ifdef HAVE_PAM +/* we need a custom PAM conversation function to handle acquiring the auth + * token (password) from the web formular, and hand it to PAM */ +int elog_conv(int num_msg, const struct pam_message **mess, struct pam_response **resp, void *my_data) +{ + char *resptok; + + /* no PAM message received, this is an error */ + if(num_msg <= 0 || num_msg >= PAM_MAX_NUM_MSG) { + *resp = NULL; + return (PAM_CONV_ERR); + } + + /* if we do not have enough space to allocate the response, we have an error + * */ + if((*resp = (pam_response*) calloc(num_msg, sizeof(struct pam_response))) == NULL) + return (PAM_BUF_ERR); + + /* this is the password we got through the UI, this is put into the + * response, and given to pam_authenticate */ + if(!(resptok = strdup((const char *)my_data))) { + free(resp); + return (PAM_BUF_ERR); + } + + /* set the response to our auth token (password) */ + (*resp)->resp = resptok; + + return (PAM_SUCCESS); +} + +int auth_verify_password_pam(LOGBOOK *lbs, const char *user, const char *password, char *error_str, int error_size) +{ + pam_handle_t *pamh; + int retval; + char str[256]; + + int verified = 0; + /* use our custom conversation function */ + static struct pam_conv elog_pam_conv = { + elog_conv, + NULL + }; + + /* set conversation application data to our password */ + elog_pam_conv.appdata_ptr = (char *)password; + + /* start PAM auth procedure */ + sprintf(str, "[PAM] Starting authentication for user %s", user); + write_logfile(lbs, str); + retval = pam_start("elogd", user, &elog_pam_conv, &pamh); + + /* if we can use PAM, try to authenticate using our conversation method */ + if(retval == PAM_SUCCESS) { + retval = pam_authenticate(pamh, 0); + } + + /* if the user authenticated, see if the acc is valid */ + if(retval == PAM_SUCCESS) { + retval = pam_acct_mgmt(pamh, 0); + } + + verified = (retval == PAM_SUCCESS); + + if(verified) + sprintf(str, "[PAM] Authentication successful for user %s", user); + else + sprintf(str, "[PAM] Authentication not successful for user %s", user); + write_logfile(lbs, str); + + if(pam_end(pamh, retval) != PAM_SUCCESS) { + pamh = NULL; + strlcpy(error_str, "PAM: Error releasing authenticator", error_size); + } + + return verified; +} +#endif /* HAVE_PAM */ + +/*---- local password file routines --------------------------------*/ + +int auth_verify_password_file(LOGBOOK * lbs, const char *user, const char *password, char *error_str, + int error_size) +{ + char upwd[256], enc_pwd[256]; + + if (error_size) + *error_str = 0; + + get_user_line(lbs, (char *) user, upwd, NULL, NULL, NULL, NULL, NULL); + do_crypt(password, enc_pwd, sizeof(enc_pwd)); + + return strcmp(enc_pwd, upwd) == 0; +} + +int auth_change_password_file(LOGBOOK * lbs, const char *user, const char *new_pwd, + char *error_str, int error_size) +{ + char str[256], file_name[256], enc_pwd[256]; + PMXML_NODE node; + + if (error_size) + *error_str = 0; + + if (lbs == NULL) + lbs = get_first_lbs_with_global_passwd(); + + if (!lbs->pwd_xml_tree) + return FALSE; + + sprintf(str, "/list/user[name=%s]/password", user); + node = mxml_find_node(lbs->pwd_xml_tree, str); + if (node == NULL) + return FALSE; + + do_crypt(new_pwd, enc_pwd, sizeof(enc_pwd)); + mxml_replace_node_value(node, enc_pwd); + + if (get_password_file(lbs, file_name, sizeof(file_name))) + mxml_write_tree(file_name, lbs->pwd_xml_tree); + + return TRUE; +} + +/*---- common function entry points --------------------------------*/ + +int auth_verify_password(LOGBOOK * lbs, const char *user, const char *password, char *error_str, + int error_size) +{ + char str[256]; + BOOL verified; + + error_str[0] = 0; + verified = FALSE; + + /* otherwise calls with null lbs which make this procedure crash */ + if (lbs == NULL) + lbs = get_first_lbs_with_global_passwd(); + + if (lbs == NULL) + return FALSE; + getcfg(lbs->name, "Authentication", str, sizeof(str)); + +#ifdef HAVE_KRB5 + if (stristr(str, "Kerberos")) + verified = auth_verify_password_krb5(lbs, user, password, error_str, error_size); + if (verified) + return TRUE; +#endif + +#ifdef HAVE_LDAP + if (stristr(str, "LDAP")) { + verified = auth_verify_password_ldap(lbs, user, password, error_str, error_size); + + // if user not in password file (external authentication!) and "LDAP register" is allowed (>0), + // obtain user info from LDAP and add locally + if (verified) { + if (get_user_line(lbs, (char *)user, NULL, NULL, NULL, NULL, NULL, NULL) == 2) { + if (getcfg(lbs->name, "LDAP register", str, sizeof(str)) && atoi(str) > 0) + ldap_adduser_file(lbs, user, password, error_str, error_size); + } + } + + ldap_clear(); + } + if (verified) + return TRUE; +#endif + +#ifdef HAVE_PAM + if(stristr(str, "PAM")) + verified = auth_verify_password_pam(lbs, user, password, error_str, error_size); + if(verified) + return TRUE; +#endif /* HAVE_PAM */ + + if (str[0] == 0 || stristr(str, "File")) + verified = auth_verify_password_file(lbs, user, password, error_str, error_size); + + return verified; +} + +int auth_change_password(LOGBOOK * lbs, const char *user, const char *old_pwd, const char *new_pwd, + char *error_str, int error_size) +{ + int status = 0; + char str[256]; + + error_str[0] = 0; + getcfg(lbs->name, "Authentication", str, sizeof(str)); + + if (str[0] == 0 || stristr(str, "File")) + status = auth_change_password_file(lbs, user, new_pwd, error_str, error_size); + + if (old_pwd) {} // avoid compiler warning + + /* Disable Kerberos password change to behave the same as for LDAP +#ifdef HAVE_KRB5 + if (stristr(str, "Kerberos")) + status = auth_change_password_krb5(lbs, user, old_pwd, new_pwd, error_str, error_size); +#endif + */ + + return status; +} diff --git a/src/elogd.cxx b/src/elogd.cxx index 0d9dc393..0b64233c 100755 --- a/src/elogd.cxx +++ b/src/elogd.cxx @@ -8813,7 +8813,7 @@ void show_change_pwd_page(LOGBOOK *lbs) { if (old_pwd[0] || new_pwd[0]) { if (user[0]) { - if (stristr(auth, "Kerberos") || stristr(auth, "Webserver") || stristr(auth, "PAM")) { + if (stristr(auth, "Webserver") || stristr(auth, "PAM")) { if (strcmp(new_pwd, new_pwd2) != 0) wrong_pwd = 2; } else { @@ -13883,7 +13883,7 @@ void show_config_page(LOGBOOK *lbs) { getcfg(lbs->name, "Authentication", auth, sizeof(auth)); strencode2(str, user, sizeof(str)); - if (stristr(auth, "Kerberos") || stristr(auth, "Webserver") || stristr(auth, "PAM")) + if (stristr(auth, "Webserver") || stristr(auth, "PAM")) rsprintf("\n", str); else rsprintf("\n", str); @@ -14209,7 +14209,7 @@ void show_forgot_pwd_page(LOGBOOK *lbs) { /*---- header ----*/ getcfg(lbs->name, "Authentication", str, sizeof(str)); - if (stristr(str, "Kerberos") || stristr(str, "Webserver") || stristr(str, "PAM")) { + if (stristr(str, "Webserver") || stristr(str, "PAM")) { show_error ("This installation of ELOG uses site authentication\nwhere password recovery is not possible"); return; @@ -14277,7 +14277,7 @@ void show_new_user_page(LOGBOOK *lbs, char *user) { rsprintf("Email:\n"); rsprintf("\n"); getcfg(lbs->name, "Authentication", str, sizeof(str)); - if (!stristr(str, "Kerberos") && !stristr(str, "Webserver") && !stristr(str, "PAM")) { + if (!stristr(str, "Webserver") && !stristr(str, "PAM")) { rsprintf("%s:\n", loc("Password")); rsprintf("\n"); @@ -29924,7 +29924,7 @@ void server_loop(void) { } #endif #ifndef HAVE_LDAP - /* check for Kerberos authentication */ + /* check for LDAP authentication */ /* NPA change */ getcfg("global", "Authentication", str, sizeof(str)); if (stristr(str, "LDAP")) {