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