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