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