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