1 /*-------------------------------------------------------------------------
2 *
3 * fe-gssapi-common.c
4 * The front-end (client) GSSAPI common code
5 *
6 * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 * IDENTIFICATION
10 * src/interfaces/libpq/fe-gssapi-common.c
11 *-------------------------------------------------------------------------
12 */
13
14 #include "postgres_fe.h"
15
16 #include "fe-gssapi-common.h"
17
18 #include "libpq-int.h"
19 #include "pqexpbuffer.h"
20
21 /*
22 * Fetch all errors of a specific type and append to "str".
23 * Each error string is preceded by a space.
24 */
25 static void
pg_GSS_error_int(PQExpBuffer str,OM_uint32 stat,int type)26 pg_GSS_error_int(PQExpBuffer str, OM_uint32 stat, int type)
27 {
28 OM_uint32 lmin_s;
29 gss_buffer_desc lmsg;
30 OM_uint32 msg_ctx = 0;
31
32 do
33 {
34 if (gss_display_status(&lmin_s, stat, type, GSS_C_NO_OID,
35 &msg_ctx, &lmsg) != GSS_S_COMPLETE)
36 break;
37 appendPQExpBufferChar(str, ' ');
38 appendBinaryPQExpBuffer(str, lmsg.value, lmsg.length);
39 gss_release_buffer(&lmin_s, &lmsg);
40 } while (msg_ctx);
41 }
42
43 /*
44 * GSSAPI errors contain two parts; put both into conn->errorMessage.
45 */
46 void
pg_GSS_error(const char * mprefix,PGconn * conn,OM_uint32 maj_stat,OM_uint32 min_stat)47 pg_GSS_error(const char *mprefix, PGconn *conn,
48 OM_uint32 maj_stat, OM_uint32 min_stat)
49 {
50 appendPQExpBuffer(&conn->errorMessage, "%s:", mprefix);
51 pg_GSS_error_int(&conn->errorMessage, maj_stat, GSS_C_GSS_CODE);
52 appendPQExpBufferChar(&conn->errorMessage, ':');
53 pg_GSS_error_int(&conn->errorMessage, min_stat, GSS_C_MECH_CODE);
54 appendPQExpBufferChar(&conn->errorMessage, '\n');
55 }
56
57 /*
58 * Check if we can acquire credentials at all (and yield them if so).
59 */
60 bool
pg_GSS_have_cred_cache(gss_cred_id_t * cred_out)61 pg_GSS_have_cred_cache(gss_cred_id_t *cred_out)
62 {
63 OM_uint32 major,
64 minor;
65 gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
66
67 major = gss_acquire_cred(&minor, GSS_C_NO_NAME, 0, GSS_C_NO_OID_SET,
68 GSS_C_INITIATE, &cred, NULL, NULL);
69 if (major != GSS_S_COMPLETE)
70 {
71 *cred_out = NULL;
72 return false;
73 }
74 *cred_out = cred;
75 return true;
76 }
77
78 /*
79 * Try to load service name for a connection
80 */
81 int
pg_GSS_load_servicename(PGconn * conn)82 pg_GSS_load_servicename(PGconn *conn)
83 {
84 OM_uint32 maj_stat,
85 min_stat;
86 int maxlen;
87 gss_buffer_desc temp_gbuf;
88 char *host;
89
90 if (conn->gtarg_nam != NULL)
91 /* Already taken care of - move along */
92 return STATUS_OK;
93
94 host = PQhost(conn);
95 if (!(host && host[0] != '\0'))
96 {
97 appendPQExpBufferStr(&conn->errorMessage,
98 libpq_gettext("host name must be specified\n"));
99 return STATUS_ERROR;
100 }
101
102 /*
103 * Import service principal name so the proper ticket can be acquired by
104 * the GSSAPI system.
105 */
106 maxlen = strlen(conn->krbsrvname) + strlen(host) + 2;
107 temp_gbuf.value = (char *) malloc(maxlen);
108 if (!temp_gbuf.value)
109 {
110 appendPQExpBufferStr(&conn->errorMessage,
111 libpq_gettext("out of memory\n"));
112 return STATUS_ERROR;
113 }
114 snprintf(temp_gbuf.value, maxlen, "%s@%s",
115 conn->krbsrvname, host);
116 temp_gbuf.length = strlen(temp_gbuf.value);
117
118 maj_stat = gss_import_name(&min_stat, &temp_gbuf,
119 GSS_C_NT_HOSTBASED_SERVICE, &conn->gtarg_nam);
120 free(temp_gbuf.value);
121
122 if (maj_stat != GSS_S_COMPLETE)
123 {
124 pg_GSS_error(libpq_gettext("GSSAPI name import error"),
125 conn,
126 maj_stat, min_stat);
127 return STATUS_ERROR;
128 }
129 return STATUS_OK;
130 }
131