diff options
Diffstat (limited to '')
-rw-r--r-- | src/interfaces/libpq/fe-gssapi-common.c | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/src/interfaces/libpq/fe-gssapi-common.c b/src/interfaces/libpq/fe-gssapi-common.c new file mode 100644 index 0000000..9e8aeae --- /dev/null +++ b/src/interfaces/libpq/fe-gssapi-common.c @@ -0,0 +1,130 @@ +/*------------------------------------------------------------------------- + * + * fe-gssapi-common.c + * The front-end (client) GSSAPI common code + * + * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/interfaces/libpq/fe-gssapi-common.c + *------------------------------------------------------------------------- + */ + +#include "postgres_fe.h" + +#include "fe-gssapi-common.h" + +#include "libpq-int.h" +#include "pqexpbuffer.h" + +/* + * Fetch all errors of a specific type and append to "str". + * Each error string is preceded by a space. + */ +static void +pg_GSS_error_int(PQExpBuffer str, OM_uint32 stat, int type) +{ + OM_uint32 lmin_s; + gss_buffer_desc lmsg; + OM_uint32 msg_ctx = 0; + + do + { + if (gss_display_status(&lmin_s, stat, type, GSS_C_NO_OID, + &msg_ctx, &lmsg) != GSS_S_COMPLETE) + break; + appendPQExpBufferChar(str, ' '); + appendBinaryPQExpBuffer(str, lmsg.value, lmsg.length); + gss_release_buffer(&lmin_s, &lmsg); + } while (msg_ctx); +} + +/* + * GSSAPI errors contain two parts; put both into conn->errorMessage. + */ +void +pg_GSS_error(const char *mprefix, PGconn *conn, + OM_uint32 maj_stat, OM_uint32 min_stat) +{ + appendPQExpBuffer(&conn->errorMessage, "%s:", mprefix); + pg_GSS_error_int(&conn->errorMessage, maj_stat, GSS_C_GSS_CODE); + appendPQExpBufferChar(&conn->errorMessage, ':'); + pg_GSS_error_int(&conn->errorMessage, min_stat, GSS_C_MECH_CODE); + appendPQExpBufferChar(&conn->errorMessage, '\n'); +} + +/* + * Check if we can acquire credentials at all (and yield them if so). + */ +bool +pg_GSS_have_cred_cache(gss_cred_id_t *cred_out) +{ + OM_uint32 major, + minor; + gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; + + major = gss_acquire_cred(&minor, GSS_C_NO_NAME, 0, GSS_C_NO_OID_SET, + GSS_C_INITIATE, &cred, NULL, NULL); + if (major != GSS_S_COMPLETE) + { + *cred_out = NULL; + return false; + } + *cred_out = cred; + return true; +} + +/* + * Try to load service name for a connection + */ +int +pg_GSS_load_servicename(PGconn *conn) +{ + OM_uint32 maj_stat, + min_stat; + int maxlen; + gss_buffer_desc temp_gbuf; + char *host; + + if (conn->gtarg_nam != NULL) + /* Already taken care of - move along */ + return STATUS_OK; + + host = PQhost(conn); + if (!(host && host[0] != '\0')) + { + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("host name must be specified\n")); + return STATUS_ERROR; + } + + /* + * Import service principal name so the proper ticket can be acquired by + * the GSSAPI system. + */ + maxlen = strlen(conn->krbsrvname) + strlen(host) + 2; + temp_gbuf.value = (char *) malloc(maxlen); + if (!temp_gbuf.value) + { + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return STATUS_ERROR; + } + snprintf(temp_gbuf.value, maxlen, "%s@%s", + conn->krbsrvname, host); + temp_gbuf.length = strlen(temp_gbuf.value); + + maj_stat = gss_import_name(&min_stat, &temp_gbuf, + GSS_C_NT_HOSTBASED_SERVICE, &conn->gtarg_nam); + free(temp_gbuf.value); + + if (maj_stat != GSS_S_COMPLETE) + { + pg_GSS_error(libpq_gettext("GSSAPI name import error"), + conn, + maj_stat, min_stat); + return STATUS_ERROR; + } + return STATUS_OK; +} |