1/* CVS GSSAPI client stuff.
2
3   This program is free software; you can redistribute it and/or modify
4   it under the terms of the GNU General Public License as published by
5   the Free Software Foundation; either version 2, or (at your option)
6   any later version.
7
8   This program is distributed in the hope that it will be useful,
9   but WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11   GNU General Public License for more details.  */
12#include <sys/cdefs.h>
13__RCSID("$NetBSD: gssapi-client.c,v 1.3 2016/05/17 14:00:09 christos Exp $");
14
15
16#ifdef HAVE_CONFIG_H
17# include <config.h>
18#endif /* HAVE_CONFIG_H */
19
20#include "cvs.h"
21#include "buffer.h"
22
23#if defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT)
24# include "gssapi-client.h"
25
26/* This is needed for GSSAPI encryption.  */
27gss_ctx_id_t gcontext;
28#endif /* CLIENT_SUPPORT || SERVER_SUPPORT */
29
30#ifdef CLIENT_SUPPORT
31# include "socket-client.h"
32
33# ifdef ENCRYPTION
34/* Whether to encrypt GSSAPI communication.  We use a global variable
35   like this because we use the same buffer type (gssapi_wrap) to
36   handle both authentication and encryption, and we don't want
37   multiple instances of that buffer in the communication stream.  */
38int cvs_gssapi_encrypt;
39# endif /* ENCRYPTION */
40
41
42/* Receive a given number of bytes.  */
43
44static void
45recv_bytes (int sock, char *buf, int need)
46{
47    while (need > 0)
48    {
49	int got;
50
51	got = recv (sock, buf, need, 0);
52	if (got <= 0)
53	    error (1, 0, "recv() from server %s: %s",
54                   current_parsed_root->hostname,
55		   got == 0 ? "EOF" : SOCK_STRERROR (SOCK_ERRNO));
56
57	buf += got;
58	need -= got;
59    }
60}
61
62
63
64/* Connect to the server using GSSAPI authentication.  */
65
66/* FIXME
67 *
68 * This really needs to be rewritten to use a buffer and not a socket.
69 * This would enable gserver to work with the SSL code I'm about to commit
70 * since the SSL connection is going to look like a FIFO and not a socket.
71 *
72 * I think, basically, it will need to use buf_output and buf_read directly
73 * since I don't think there is a read_bytes function - only read_line.
74 *
75 * recv_bytes could then be removed too.
76 *
77 * Besides, I added some cruft to reenable the socket which shouldn't be
78 * there.  This would also enable its removal.
79 */
80#define BUFSIZE 1024
81int
82connect_to_gserver (cvsroot_t *root, int sock, const char *hostname)
83{
84    char *str;
85    char buf[BUFSIZE];
86    gss_buffer_desc *tok_in_ptr, tok_in, tok_out;
87    OM_uint32 stat_min, stat_maj;
88    gss_name_t server_name;
89
90    str = "BEGIN GSSAPI REQUEST\012";
91
92    if (send (sock, str, strlen (str), 0) < 0)
93	error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO));
94
95    if (strlen (hostname) > BUFSIZE - 5)
96	error (1, 0, "Internal error: hostname exceeds length of buffer");
97    snprintf (buf, sizeof(buf), "cvs@%s", hostname);
98    tok_in.length = strlen (buf);
99    tok_in.value = buf;
100    gss_import_name (&stat_min, &tok_in, GSS_C_NT_HOSTBASED_SERVICE,
101		     &server_name);
102
103    tok_in_ptr = GSS_C_NO_BUFFER;
104    gcontext = GSS_C_NO_CONTEXT;
105
106    do
107    {
108	stat_maj = gss_init_sec_context (&stat_min, GSS_C_NO_CREDENTIAL,
109					 &gcontext, server_name,
110					 GSS_C_NULL_OID,
111					 (GSS_C_MUTUAL_FLAG
112					  | GSS_C_REPLAY_FLAG),
113					 0, NULL, tok_in_ptr, NULL, &tok_out,
114					 NULL, NULL);
115	if (stat_maj != GSS_S_COMPLETE && stat_maj != GSS_S_CONTINUE_NEEDED)
116	{
117	    OM_uint32 message_context;
118	    OM_uint32 new_stat_min;
119
120	    message_context = 0;
121	    gss_display_status (&new_stat_min, stat_maj, GSS_C_GSS_CODE,
122                                GSS_C_NULL_OID, &message_context, &tok_out);
123	    error (0, 0, "GSSAPI authentication failed: %s",
124		   (const char *) tok_out.value);
125
126	    message_context = 0;
127	    gss_display_status (&new_stat_min, stat_min, GSS_C_MECH_CODE,
128				GSS_C_NULL_OID, &message_context, &tok_out);
129	    error (1, 0, "GSSAPI authentication failed: %s",
130		   (const char *) tok_out.value);
131	}
132
133	if (tok_out.length == 0)
134	{
135	    tok_in.length = 0;
136	}
137	else
138	{
139	    char cbuf[2];
140	    int need;
141
142	    cbuf[0] = (tok_out.length >> 8) & 0xff;
143	    cbuf[1] = tok_out.length & 0xff;
144	    if (send (sock, cbuf, 2, 0) < 0)
145		error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO));
146	    if (send (sock, tok_out.value, tok_out.length, 0) < 0)
147		error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO));
148
149	    recv_bytes (sock, cbuf, 2);
150	    need = ((cbuf[0] & 0xff) << 8) | (cbuf[1] & 0xff);
151
152	    if (need > sizeof buf)
153	    {
154		ssize_t got;
155		size_t total;
156
157		/* This usually means that the server sent us an error
158		   message.  Read it byte by byte and print it out.
159		   FIXME: This is a terrible error handling strategy.
160		   However, even if we fix the server, we will still
161		   want to do this to work with older servers.  */
162		buf[0] = cbuf[0];
163		buf[1] = cbuf[1];
164		total = 2;
165		while ((got = recv (sock, buf + total, sizeof buf - total, 0)))
166		{
167		    if (got < 0)
168			error (1, 0, "recv() from server %s: %s",
169			       root->hostname, SOCK_STRERROR (SOCK_ERRNO));
170		    total += got;
171		    if (strrchr (buf + total - got, '\n'))
172			break;
173		}
174		buf[total] = '\0';
175		if (buf[total - 1] == '\n')
176		    buf[total - 1] = '\0';
177		error (1, 0, "error from server %s: %s", root->hostname,
178		       buf);
179	    }
180
181	    recv_bytes (sock, buf, need);
182	    tok_in.length = need;
183	}
184
185	tok_in.value = buf;
186	tok_in_ptr = &tok_in;
187    }
188    while (stat_maj == GSS_S_CONTINUE_NEEDED);
189
190    return 1;
191}
192#endif /* CLIENT_SUPPORT */
193
194
195
196#if defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT)
197/* A buffer interface using GSSAPI.  It is built on top of a
198   packetizing buffer.  */
199
200/* This structure is the closure field of the GSSAPI translation
201   routines.  */
202
203struct cvs_gssapi_wrap_data
204{
205    /* The GSSAPI context.  */
206    gss_ctx_id_t gcontext;
207};
208
209
210
211/* Unwrap data using GSSAPI.  */
212static int
213cvs_gssapi_wrap_input (void *fnclosure, const char *input, char *output,
214                       size_t size)
215{
216    struct cvs_gssapi_wrap_data *gd = fnclosure;
217    gss_buffer_desc inbuf, outbuf;
218    OM_uint32 stat_min;
219    int conf;
220
221    inbuf.value = (void *)input;
222    inbuf.length = size;
223
224    if (gss_unwrap (&stat_min, gd->gcontext, &inbuf, &outbuf, &conf, NULL)
225	!= GSS_S_COMPLETE)
226    {
227	error (1, 0, "gss_unwrap failed");
228    }
229
230    if (outbuf.length > size)
231	abort ();
232
233    memcpy (output, outbuf.value, outbuf.length);
234
235    /* The real packet size is stored in the data, so we don't need to
236       remember outbuf.length.  */
237
238    gss_release_buffer (&stat_min, &outbuf);
239
240    return 0;
241}
242
243
244
245/* Wrap data using GSSAPI.  */
246static int
247cvs_gssapi_wrap_output (void *fnclosure, const char *input, char *output,
248                        size_t size, size_t *translated)
249{
250    struct cvs_gssapi_wrap_data *gd = fnclosure;
251    gss_buffer_desc inbuf, outbuf;
252    OM_uint32 stat_min;
253    int conf_req, conf;
254
255    inbuf.value = (void *)input;
256    inbuf.length = size;
257
258#ifdef ENCRYPTION
259    conf_req = cvs_gssapi_encrypt;
260#else
261    conf_req = 0;
262#endif
263
264    if (gss_wrap (&stat_min, gd->gcontext, conf_req, GSS_C_QOP_DEFAULT,
265		  &inbuf, &conf, &outbuf) != GSS_S_COMPLETE)
266	error (1, 0, "gss_wrap failed");
267
268    /* The packetizing buffer only permits us to add 100 bytes.
269       FIXME: I don't know what, if anything, is guaranteed by GSSAPI.
270       This may need to be increased for a different GSSAPI
271       implementation, or we may need a different algorithm.  */
272    if (outbuf.length > size + 100)
273	abort ();
274
275    memcpy (output, outbuf.value, outbuf.length);
276
277    *translated = outbuf.length;
278
279    gss_release_buffer (&stat_min, &outbuf);
280
281    return 0;
282}
283
284
285
286/* Create a GSSAPI wrapping buffer.  We use a packetizing buffer with
287   GSSAPI wrapping routines.  */
288struct buffer *
289cvs_gssapi_wrap_buffer_initialize (struct buffer *buf, int input,
290                                   gss_ctx_id_t gcontext,
291                                   void (*memory) ( struct buffer * ))
292{
293    struct cvs_gssapi_wrap_data *gd;
294
295    gd = xmalloc (sizeof *gd);
296    gd->gcontext = gcontext;
297
298    return packetizing_buffer_initialize (buf,
299                                          input ? cvs_gssapi_wrap_input
300						: NULL,
301                                          input ? NULL
302						: cvs_gssapi_wrap_output,
303                                          gd, memory);
304}
305
306
307
308void
309initialize_gssapi_buffers (struct buffer **to_server_p,
310                           struct buffer **from_server_p)
311{
312  *to_server_p = cvs_gssapi_wrap_buffer_initialize (*to_server_p, 0,
313						    gcontext, NULL);
314
315  *from_server_p = cvs_gssapi_wrap_buffer_initialize (*from_server_p, 1,
316						      gcontext, NULL);
317}
318#endif /* CLIENT_SUPPORT || SERVER_SUPPORT */
319