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