summaryrefslogtreecommitdiff
path: root/src/interfaces/libpq/fe-gssapi-common.c
blob: fa08526ee2a351c44522d6b602546abb3632bd63 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*-------------------------------------------------------------------------
 *
 * fe-gssapi-common.c
 *     The front-end (client) GSSAPI common code
 *
 * Portions Copyright (c) 1996-2022, 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;
}