1 /*-------------------------------------------------------------------------
2  *
3  * fe-gssapi-common.c
4  *     The front-end (client) GSSAPI common code
5  *
6  * Portions Copyright (c) 1996-2019, 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 	resetPQExpBuffer(&conn->errorMessage);
51 	appendPQExpBuffer(&conn->errorMessage, "%s:", mprefix);
52 	pg_GSS_error_int(&conn->errorMessage, maj_stat, GSS_C_GSS_CODE);
53 	appendPQExpBufferChar(&conn->errorMessage, ':');
54 	pg_GSS_error_int(&conn->errorMessage, min_stat, GSS_C_MECH_CODE);
55 	appendPQExpBufferChar(&conn->errorMessage, '\n');
56 }
57 
58 /*
59  * Check if we can acquire credentials at all (and yield them if so).
60  */
61 bool
pg_GSS_have_cred_cache(gss_cred_id_t * cred_out)62 pg_GSS_have_cred_cache(gss_cred_id_t *cred_out)
63 {
64 	OM_uint32	major,
65 				minor;
66 	gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
67 
68 	major = gss_acquire_cred(&minor, GSS_C_NO_NAME, 0, GSS_C_NO_OID_SET,
69 							 GSS_C_INITIATE, &cred, NULL, NULL);
70 	if (major != GSS_S_COMPLETE)
71 	{
72 		*cred_out = NULL;
73 		return false;
74 	}
75 	*cred_out = cred;
76 	return true;
77 }
78 
79 /*
80  * Try to load service name for a connection
81  */
82 int
pg_GSS_load_servicename(PGconn * conn)83 pg_GSS_load_servicename(PGconn *conn)
84 {
85 	OM_uint32	maj_stat,
86 				min_stat;
87 	int			maxlen;
88 	gss_buffer_desc temp_gbuf;
89 	char	   *host;
90 
91 	if (conn->gtarg_nam != NULL)
92 		/* Already taken care of - move along */
93 		return STATUS_OK;
94 
95 	host = PQhost(conn);
96 	if (!(host && host[0] != '\0'))
97 	{
98 		printfPQExpBuffer(&conn->errorMessage,
99 						  libpq_gettext("host name must be specified\n"));
100 		return STATUS_ERROR;
101 	}
102 
103 	/*
104 	 * Import service principal name so the proper ticket can be acquired by
105 	 * the GSSAPI system.
106 	 */
107 	maxlen = strlen(conn->krbsrvname) + strlen(host) + 2;
108 	temp_gbuf.value = (char *) malloc(maxlen);
109 	if (!temp_gbuf.value)
110 	{
111 		printfPQExpBuffer(&conn->errorMessage,
112 						  libpq_gettext("out of memory\n"));
113 		return STATUS_ERROR;
114 	}
115 	snprintf(temp_gbuf.value, maxlen, "%s@%s",
116 			 conn->krbsrvname, host);
117 	temp_gbuf.length = strlen(temp_gbuf.value);
118 
119 	maj_stat = gss_import_name(&min_stat, &temp_gbuf,
120 							   GSS_C_NT_HOSTBASED_SERVICE, &conn->gtarg_nam);
121 	free(temp_gbuf.value);
122 
123 	if (maj_stat != GSS_S_COMPLETE)
124 	{
125 		pg_GSS_error(libpq_gettext("GSSAPI name import error"),
126 					 conn,
127 					 maj_stat, min_stat);
128 		return STATUS_ERROR;
129 	}
130 	return STATUS_OK;
131 }
132