diff options
Diffstat (limited to 'framework/src/audit/audisp/plugins/zos-remote/zos-remote-ldap.c')
-rw-r--r-- | framework/src/audit/audisp/plugins/zos-remote/zos-remote-ldap.c | 608 |
1 files changed, 608 insertions, 0 deletions
diff --git a/framework/src/audit/audisp/plugins/zos-remote/zos-remote-ldap.c b/framework/src/audit/audisp/plugins/zos-remote/zos-remote-ldap.c new file mode 100644 index 00000000..209743f3 --- /dev/null +++ b/framework/src/audit/audisp/plugins/zos-remote/zos-remote-ldap.c @@ -0,0 +1,608 @@ +/*************************************************************************** + * Copyright (C) 2007 International Business Machines Corp. * + * All Rights Reserved. * + * * + * This program 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 2 of the License, or * + * (at your option) any later version. * + * * + * This program 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 this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + * * + * Authors: * + * Klaus Heinrich Kiwi <klausk@br.ibm.com> * + ***************************************************************************/ + +#include "zos-remote-ldap.h" + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "zos-remote-log.h" + +/*************************************************************************** + * Audit response struct * + ***************************************************************************/ +typedef struct audit_resp_item +{ + ber_int_t version; /* Version of Response data itself */ + ber_int_t itemTag; /* Copy of itemTag from Operation */ + ber_int_t majorCode; /* Majorcode. Main return code of this Outcome */ + ber_int_t minorCode1; /* minorCode1. SAFRc or other Rc */ + ber_int_t minorCode2; /* minorCode2. RacfRc or other Rc */ + ber_int_t minorCode3; /* minorCode3. RacfRsn or other Rc */ +} audit_resp_item_t; + +typedef struct audit_response +{ + ber_int_t respVersion; /* Overall version */ + ber_int_t respMajor; /* Overall major code */ + unsigned int numItems; /* Number of response items */ + audit_resp_item_t **itemList; /* response ItemList */ +} audit_response_t; + + +/*************************************************************************** + * z/OS Remote-services Major return code handling * + ***************************************************************************/ +struct zos_remote_error +{ + int code; + char *str; +}; + +static struct zos_remote_error zos_remote_errlist[] = { + {ZOS_REMOTE_MAJOR_SUCCESS, "Success"}, + {ZOS_REMOTE_MAJOR_WARNINGMODE, "WARNINGMODE - Event was logged, with warnings"}, + {ZOS_REMOTE_MAJOR_NOTREQ, "NOTREQ - No logging required"}, + {ZOS_REMOTE_MAJOR_UNDETERMINED, "UNDETERMINED - Undetermined result"}, + {ZOS_REMOTE_MAJOR_UNAUTHORIZED, "UNAUTHORIZED - The user does not have authority the R_auditx service"}, + {ZOS_REMOTE_MAJOR_RACROUTE, "RACROUTE - The R_auditx service returned an unexpected error"}, + {ZOS_REMOTE_MAJOR_VAL_ERR, "VAL_ERR - Value error in request"}, + {ZOS_REMOTE_MAJOR_ENC_ERR, "ENC_ERR - DER decoding error in request"}, + {ZOS_REMOTE_MAJOR_UNSUF_AUTH, "UNSUF_AUTH - The user has unsuficient authority for the requested function"}, + {ZOS_REMOTE_MAJOR_EMPTY, "EMPTY - Empty request received - No items found within the ItemList"}, + {ZOS_REMOTE_MAJOR_INVALID_VER, "INVALID_VER - Invalid RequestVersion"}, + {ZOS_REMOTE_MAJOR_INTERNAL_ERR, "INTERNAL_ERR - An internal error was encountered within the ICTX component"}, + {-1, NULL} +}; + +/*************************************************************************** + * Internal functions prototypes * + ***************************************************************************/ +static int _zos_remote_init(ZOS_REMOTE *); +static void _zos_remote_destroy(ZOS_REMOTE *); +static int zos_remote_connect(ZOS_REMOTE *); +static void zos_remote_disconnect(ZOS_REMOTE *); +static int submit_xop_s(ZOS_REMOTE *, struct berval *); +static int decode_response(audit_response_t *, struct berval *); + +/*************************************************************************** + * Exported functions * + ***************************************************************************/ +int submit_request_s(ZOS_REMOTE *zos_remote, BerElement *ber) +{ + int rc, retry = 1; /* retry once and give up */ + struct berval bv; + + rc = ber_flatten2(ber, &bv, 0); /* 0 = Use ber's buffer */ + if (rc == -1) { + log_err("Error flattening BER element"); + return ICTX_E_ABORT; + } + +retry: + rc = submit_xop_s(zos_remote, &bv); + switch (rc) { + case ICTX_SUCCESS: + break; + case ICTX_E_TRYAGAIN: + /* + * Usually means that the server connection timed-out + * So we flush the LDAP connection by unsetting the + * 'connected' flag and trying again. + */ + if (retry > 0) { + log_debug("Connection seems down - retrying"); + retry--; + _zos_remote_destroy(zos_remote); + rc = _zos_remote_init(zos_remote); + if (rc != ICTX_SUCCESS) + log_err("Error - failed to re-initialize LDAP session"); + else + goto retry; /* go to submit_xop_s once more */ + } + log_err("Can't establish connection"); + break; + case ICTX_E_ABORT: + break; + default: + log_err("Event resulted failure, code: 0x%x", rc); + } + + return rc; +} + +int zos_remote_init(ZOS_REMOTE *zos_remote, const char *server, int port, + const char *user, const char *password, int timeout) +{ + zos_remote->server = strdup(server); + zos_remote->port = port; + zos_remote->user = strdup(user); + zos_remote->password = strdup(password); + zos_remote->timeout = timeout; + zos_remote->connected = 0; + + if (!zos_remote->server || !zos_remote->user || !zos_remote->password) { + log_err("Error allocating memory for session members"); + return ICTX_E_FATAL; + } + + return _zos_remote_init(zos_remote); +} + +void zos_remote_destroy(ZOS_REMOTE *zos_remote) +{ + _zos_remote_destroy(zos_remote); + + free(zos_remote->server); + free(zos_remote->user); + free(zos_remote->password); +} + +char *zos_remote_err2string(int err) +{ + int i; + + for (i = 0; zos_remote_errlist[i].str != NULL; i++) { + if (err == zos_remote_errlist[i].code) + return zos_remote_errlist[i].str; + } + return "Unknown error"; +} + +/*************************************************************************** + * Internal Functions * + ***************************************************************************/ +static int _zos_remote_init(ZOS_REMOTE *zos_remote) +{ + int version, rc; + char *uri = NULL; + +#ifdef LDAP_DEPRECATED + + log_debug("Initializing z/OS Remote-services LDAP connection at ldap://%s:%d", + zos_remote->server, zos_remote->port); + zos_remote->ld = ldap_init(zos_remote->server + zos_remote->port ? zos_remote->port : LDAP_PORT); + if (zos_remote->ld == NULL) { + log_err("Error initializing LDAP session: %s", + strerror(errno)); + rc = ICTX_E_FATAL; + goto end; + } +#else + /* build ldap URI */ + if (zos_remote->port == 0 || zos_remote->port == LDAP_PORT) + rc = asprintf(&uri, "ldap://%s", zos_remote->server); + else + rc = asprintf(&uri, "ldap://%s:%d", zos_remote->server, + zos_remote->port); + + if (rc == -1) { + log_err("Out of memory building LDAP server URI"); + rc = ICTX_E_FATAL; + uri = NULL; + goto end; + } + + log_debug("Initializing z/OS Remote-services LDAP connection at %s", uri); + /* Get a handle to an LDAP connection */ + rc = ldap_initialize(&zos_remote->ld, uri); + if (rc != LDAP_SUCCESS) { + log_err("Error initializing LDAP session: %s", + ldap_err2string(rc)); + rc = ICTX_E_FATAL; + goto free_uri; + } +#endif + + /* + * Ensure the LDAP protocol version supported by the client + * to 3. (Extended operations are part of version 3). + */ + rc = ldap_get_option(zos_remote->ld, LDAP_OPT_PROTOCOL_VERSION, + &version); + if (rc != LDAP_OPT_SUCCESS) { + log_err("Error getting LDAP session options"); + rc = ICTX_E_FATAL; + goto unbind; + } + + if (version < LDAP_VERSION3) { + log_debug("Setting LDAP session version to %d", + LDAP_VERSION3); + version = LDAP_VERSION3; + rc = ldap_set_option(zos_remote->ld, LDAP_OPT_PROTOCOL_VERSION, + &version); + if (rc != LDAP_OPT_SUCCESS) { + log_err("Error setting LDAP session version"); + rc = ICTX_E_FATAL; + goto unbind; + } + } + + goto free_uri; + +unbind: + ldap_unbind_ext_s(zos_remote->ld, NULL, NULL); + zos_remote->ld = NULL; + +free_uri: + free(uri); + +end: + return rc; +} + +static void _zos_remote_destroy(ZOS_REMOTE *zos_remote) +{ + zos_remote_disconnect(zos_remote); + zos_remote->ld = NULL; +} + +static int zos_remote_connect(ZOS_REMOTE *zos_remote) +{ + struct berval cred; + int rc; + char bindusr[255]; + + snprintf(bindusr, 255, "racfid=%s,cn=ictx", zos_remote->user); + + log_debug("Attempting BIND. User '%s', password '<not shown>'", + bindusr); + + cred.bv_val = (char *) zos_remote->password; + cred.bv_len = strlen(zos_remote->password); + + rc = ldap_sasl_bind_s(zos_remote->ld, bindusr, + LDAP_SASL_SIMPLE, &cred, + NULL, NULL, NULL); + + + switch (rc) { + case LDAP_SUCCESS: + log_debug("LDAP BIND succeeded"); + zos_remote->connected = 1; + rc = ICTX_SUCCESS; + break; + case LDAP_SERVER_DOWN: + case LDAP_BUSY: + case LDAP_UNAVAILABLE: + case LDAP_TIMEOUT: + case LDAP_CONNECT_ERROR: + log_warn("z/OS Remote-services connection failed: %s", + ldap_err2string(rc)); + rc = ICTX_E_TRYAGAIN; + break; + default: + log_err("Error - z/OS Remote-services initialization failed: %s", + ldap_err2string(rc)); + rc = ICTX_E_FATAL; + } + + return rc; +} + + +static void zos_remote_disconnect(ZOS_REMOTE *zos_remote) +{ + if (zos_remote->ld) { + log_debug("Unbinding LDAP session"); + +#ifdef LDAP_DEPRECATED + ldap_unbind(zos_remote->ld); +#else + ldap_unbind_ext_s(zos_remote->ld, NULL, NULL); +#endif + } + zos_remote->connected = 0; + +} + +/* + * Sync-submit extended operation given in *bv + * return ICTX_SUCCESS if submission (and response) + * succeeded. + * Log errors using log_err() functions + */ +int submit_xop_s(ZOS_REMOTE *zos_remote, struct berval *bv) +{ + LDAPMessage *result; + audit_response_t response; + int rc, errcode, msgId; + unsigned int i; + char *errmsg, *oid; + struct berval *bv_response; + struct timeval t; + + if (zos_remote->connected == 0) { + rc = zos_remote_connect(zos_remote); + if (rc != ICTX_SUCCESS) + return rc; + } + + /* call LDAP - won't block */ + rc = ldap_extended_operation(zos_remote->ld, ICTX_OIDAUDITREQUEST, + bv, NULL, NULL, &msgId); + if (rc == LDAP_SERVER_DOWN) { + zos_remote->connected = 0; + return ICTX_E_TRYAGAIN; + } else if (rc != LDAP_SUCCESS) { + log_err("LDAP extended operation submission failure: %s", + ldap_err2string(rc)); + return ICTX_E_ABORT; + } else { + log_debug("Sent LDAP extended operation request, msgId=0x%x", + msgId); + } + + /* call blocking ldap_result with specified timeout */ + t.tv_sec = zos_remote->timeout; + t.tv_usec = 0; + rc = ldap_result(zos_remote->ld, msgId, 1, &t, &result); + + if (rc == -1) { + /* error in ldap operation */ + ldap_get_option(zos_remote->ld, LDAP_OPT_ERROR_NUMBER, &errcode); + switch (errcode) { + case LDAP_SERVER_DOWN: + /* Connection may have timed out, let's retry */ + zos_remote->connected = 0; + rc = ICTX_E_TRYAGAIN; + break; + default: + log_err("ldap_result unexpected failure: %s (0x%x)", + ldap_err2string(rc), rc); + rc = ICTX_E_ABORT; + } + goto end; + } else if (rc == 0) { + /* timeout reached */ + log_warn("LDAP extended operation timed out"); + rc = ICTX_E_ABORT; + goto end; + } else if (rc != LDAP_RES_EXTENDED) { + /* not an extended operation response! */ + log_err("LDAP extended operation resulted in unexpected answer: 0x%x", rc); + rc = ICTX_E_ABORT; + goto free_result; + } + + log_debug("Got LDAP Extended result"); + /* + * we have an extended operation result + * first parse_result will check for errcode, later + * parse_extended_result will give us the oid and the BER value + */ + rc = ldap_parse_result(zos_remote->ld, result, &errcode, NULL, + &errmsg, NULL, NULL, 0); + if (rc != LDAP_SUCCESS) { + log_err("LDAP parse result internal failure (code 0x%x)", + rc); + rc = ICTX_E_ABORT; + goto free_result; + } + + if (errcode != LDAP_SUCCESS) { + log_err("LDAP extended operation failed: %s", errmsg); + rc = ICTX_E_ABORT; + goto free_errmsg; + } + + rc = ldap_parse_extended_result(zos_remote->ld, result, &oid, + &bv_response, 0); + if (rc != LDAP_SUCCESS) { + log_err("Failed to parse ldap extended result (code 0x%x)", + rc); + rc = ICTX_E_ABORT; + goto free_errmsg; + } + + if (oid && strcmp(oid, ICTX_OIDAUDITRESPONSE) != 0) { + /* oid == null shouldn't be a problem to log_err */ + log_err("LDAP extended operation returned an invalid oid: %s", oid); + rc = ICTX_E_ABORT; + goto free_bv; + } + + rc = decode_response(&response, bv_response); + if (rc != ICTX_SUCCESS) { + log_err("Error decoding extended operation response"); + goto free_bv; + } + + if (response.respMajor == ZOS_REMOTE_MAJOR_SUCCESS) { + /* submission was successful, no further processing needed */ + log_debug("Successfully submited Remote audit Request"); + rc = ICTX_SUCCESS; + goto free_response; + } else if (response.respMajor == ZOS_REMOTE_MAJOR_EMPTY) { + /* something is going on. Set error and stop processing */ + log_warn("Warning - LDAP extended operation returned empty result"); + rc = ICTX_E_ABORT; + goto free_response; + } else if (response.respMajor == ZOS_REMOTE_MAJOR_WARNINGMODE || + response.respMajor == ZOS_REMOTE_MAJOR_NOTREQ) + rc = ICTX_SUCCESS; /* don't fail, but continue processing */ + else + rc = ICTX_E_ABORT; /* set return code and continue processing */ + + /* If it's not success nor empty, let's check for errors in the response */ + for (i = 0; i < response.numItems; i++) { + switch ((response.itemList[i])->majorCode) { + /* 0 <= Major Code <= 14 */ + case ZOS_REMOTE_MAJOR_SUCCESS: + break; + case ZOS_REMOTE_MAJOR_WARNINGMODE: + case ZOS_REMOTE_MAJOR_NOTREQ: + log_debug("Warning - LDAP extended operation returned '%s' for item %d", + zos_remote_err2string((response.itemList[i])->majorCode), + (response.itemList[i])->itemTag); + log_debug("SAF code: 0x%x, RACF code: 0x%x, RACF reason: 0x%x", + (response.itemList[i])->minorCode1, + (response.itemList[i])->minorCode2, + (response.itemList[i])->minorCode3); + break; + case ZOS_REMOTE_MAJOR_UNDETERMINED: + case ZOS_REMOTE_MAJOR_UNAUTHORIZED: + case ZOS_REMOTE_MAJOR_RACROUTE: + log_err("Error - LDAP extended operation returned '%s' for item %d", + zos_remote_err2string((response.itemList[i])->majorCode), + (response.itemList[i])->itemTag); + log_err("SAF code: 0x%x, RACF code: 0x%x, RACF reason: 0x%x", + (response.itemList[i])->minorCode1, + (response.itemList[i])->minorCode2, + (response.itemList[i])->minorCode3); + break; + /* 16 <= Major Code <= 20 */ + case ZOS_REMOTE_MAJOR_VAL_ERR: + case ZOS_REMOTE_MAJOR_ENC_ERR: + log_err("Error - LDAP extended operation returned '%s' for item %d", + zos_remote_err2string((response.itemList[i])->majorCode), + (response.itemList[i])->itemTag); + log_err("Item field: %d, reson %d", + (response.itemList[i])-> + minorCode1, + (response.itemList[i])->minorCode2); + break; + /* 24 <= Major code <= 100 */ + case ZOS_REMOTE_MAJOR_UNSUF_AUTH: + case ZOS_REMOTE_MAJOR_EMPTY: + case ZOS_REMOTE_MAJOR_INVALID_VER: + case ZOS_REMOTE_MAJOR_INTERNAL_ERR: + log_err("Error - LDAP extended operation returned '%s' for item %d", + zos_remote_err2string((response.itemList[i])->majorCode), + (response.itemList[i])->itemTag); + break; + default: + log_err("Error - LDAP extended operation returned an unknown Major code for item %d", + (response.itemList[i])->majorCode); + } + } + +free_response: + for (; response.numItems > 0; response.numItems--) + free(response.itemList[response.numItems - 1]); + free(response.itemList); + +free_bv: + if (bv_response) + ber_bvfree(bv_response); + if (oid) + ldap_memfree(oid); + +free_errmsg: + ldap_memfree(errmsg); + +free_result: + ldap_msgfree(result); + +end: + return rc; +} + +static int decode_response(audit_response_t * r, struct berval *bv) +{ + BerElement *ber; + ber_len_t len; + int rc; + + if (!bv) { + log_err("LDAP extended operation returned NULL message"); + return ICTX_E_ABORT; + } else if ((ber = ber_init(bv)) == NULL) { + log_err("Error initializing BER response data"); + return ICTX_E_ABORT; + } + + log_debug("---Got an encoded request response:"); + debug_bv(bv); + + r->respVersion = 0; + r->respMajor = 0; + r->numItems = 0; + r->itemList = NULL; + + rc = ber_scanf(ber, "{ii", &r->respVersion, &r->respMajor); + if (r->respVersion != ICTX_REQUESTVER) { + log_err("Invalid version returned by z/OS Remote-services server"); + log_err("Should be %d, got %d", ICTX_REQUESTVER, + r->respVersion); + rc = ICTX_E_ABORT; + goto free_ber; + } + + if (r->respMajor == ZOS_REMOTE_MAJOR_SUCCESS || + r->respMajor == ZOS_REMOTE_MAJOR_EMPTY) { + rc = ICTX_SUCCESS; + /* No further processing required */ + goto free_ber; + } + + /* Inspect ber response otherwise */ + while (ber_peek_tag(ber, &len) == LBER_SEQUENCE) { + r->numItems++; + r->itemList = (audit_resp_item_t **) realloc(r->itemList, + r->numItems * + sizeof + (audit_resp_item_t + *)); + if (errno == ENOMEM) { + if (r->itemList) + free(r->itemList); + rc = ICTX_E_FATAL; + goto free_ber; + } + + audit_resp_item_t *item = (audit_resp_item_t *) + malloc(sizeof(audit_resp_item_t)); + + if (!item) { + rc = ICTX_E_FATAL; + goto free_ber; + } + + rc |= ber_scanf(ber, "{{iiiiii}}", + &item->version, + &item->itemTag, + &item->majorCode, + &item->minorCode1, &item->minorCode2, + &item->minorCode3); + r->itemList[r->numItems - 1] = item; + } + rc |= ber_scanf(ber, "}"); + + if (rc == -1) { + for (; r->numItems > 0; r->numItems--) + free(r->itemList[r->numItems - 1]); + free(r->itemList); + rc = ICTX_E_ABORT; + } + else + rc = ICTX_SUCCESS; + +free_ber: + ber_free(ber, 1); + + return rc; +} |