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. */
27 gss_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. */
38 int cvs_gssapi_encrypt;
39 # endif /* ENCRYPTION */
40
41
42 /* Receive a given number of bytes. */
43
44 static void
recv_bytes(int sock,char * buf,int need)45 recv_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
81 int
connect_to_gserver(cvsroot_t * root,int sock,const char * hostname)82 connect_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
203 struct cvs_gssapi_wrap_data
204 {
205 /* The GSSAPI context. */
206 gss_ctx_id_t gcontext;
207 };
208
209
210
211 /* Unwrap data using GSSAPI. */
212 static int
cvs_gssapi_wrap_input(void * fnclosure,const char * input,char * output,size_t size)213 cvs_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. */
246 static int
cvs_gssapi_wrap_output(void * fnclosure,const char * input,char * output,size_t size,size_t * translated)247 cvs_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. */
288 struct buffer *
cvs_gssapi_wrap_buffer_initialize(struct buffer * buf,int input,gss_ctx_id_t gcontext,void (* memory)(struct buffer *))289 cvs_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
308 void
initialize_gssapi_buffers(struct buffer ** to_server_p,struct buffer ** from_server_p)309 initialize_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