1 /* CVS GSSAPI client stuff.
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)
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. */
16 #endif /* HAVE_CONFIG_H */
21 #if defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT)
22 # include "gssapi-client.h"
24 /* This is needed for GSSAPI encryption. */
25 gss_ctx_id_t gcontext
;
26 #endif /* CLIENT_SUPPORT || SERVER_SUPPORT */
29 # include "socket-client.h"
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 */
40 /* Receive a given number of bytes. */
43 recv_bytes (int sock
, char *buf
, int need
)
49 got
= recv (sock
, buf
, need
, 0);
51 error (1, 0, "recv() from server %s: %s",
52 current_parsed_root
->hostname
,
53 got
== 0 ? "EOF" : SOCK_STRERROR (SOCK_ERRNO
));
62 /* Connect to the server using GSSAPI authentication. */
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.
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.
73 * recv_bytes could then be removed too.
75 * Besides, I added some cruft to reenable the socket which shouldn't be
76 * there. This would also enable its removal.
80 connect_to_gserver (cvsroot_t
*root
, int sock
, const char *hostname
)
84 gss_buffer_desc
*tok_in_ptr
, tok_in
, tok_out
;
85 OM_uint32 stat_min
, stat_maj
;
86 gss_name_t server_name
;
88 str
= "BEGIN GSSAPI REQUEST\012";
90 if (send (sock
, str
, strlen (str
), 0) < 0)
91 error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO
));
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
);
98 gss_import_name (&stat_min
, &tok_in
, GSS_C_NT_HOSTBASED_SERVICE
,
101 tok_in_ptr
= GSS_C_NO_BUFFER
;
102 gcontext
= GSS_C_NO_CONTEXT
;
106 stat_maj
= gss_init_sec_context (&stat_min
, GSS_C_NO_CREDENTIAL
,
107 &gcontext
, server_name
,
110 | GSS_C_REPLAY_FLAG
),
111 0, NULL
, tok_in_ptr
, NULL
, &tok_out
,
113 if (stat_maj
!= GSS_S_COMPLETE
&& stat_maj
!= GSS_S_CONTINUE_NEEDED
)
115 OM_uint32 message_context
;
116 OM_uint32 new_stat_min
;
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
);
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
);
131 if (tok_out
.length
== 0)
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
));
147 recv_bytes (sock
, cbuf
, 2);
148 need
= ((cbuf
[0] & 0xff) << 8) | (cbuf
[1] & 0xff);
150 if (need
> sizeof buf
)
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. */
163 while ((got
= recv (sock
, buf
+ total
, sizeof buf
- total
, 0)))
166 error (1, 0, "recv() from server %s: %s",
167 root
->hostname
, SOCK_STRERROR (SOCK_ERRNO
));
169 if (strrchr (buf
+ total
- got
, '\n'))
173 if (buf
[total
- 1] == '\n')
174 buf
[total
- 1] = '\0';
175 error (1, 0, "error from server %s: %s", root
->hostname
,
179 recv_bytes (sock
, buf
, need
);
180 tok_in
.length
= need
;
184 tok_in_ptr
= &tok_in
;
186 while (stat_maj
== GSS_S_CONTINUE_NEEDED
);
190 #endif /* CLIENT_SUPPORT */
194 #if defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT)
195 /* A buffer interface using GSSAPI. It is built on top of a
196 packetizing buffer. */
198 /* This structure is the closure field of the GSSAPI translation
201 struct cvs_gssapi_wrap_data
203 /* The GSSAPI context. */
204 gss_ctx_id_t gcontext
;
209 /* Unwrap data using GSSAPI. */
211 cvs_gssapi_wrap_input (void *fnclosure
, const char *input
, char *output
,
214 struct cvs_gssapi_wrap_data
*gd
= fnclosure
;
215 gss_buffer_desc inbuf
, outbuf
;
219 inbuf
.value
= (void *)input
;
222 if (gss_unwrap (&stat_min
, gd
->gcontext
, &inbuf
, &outbuf
, &conf
, NULL
)
225 error (1, 0, "gss_unwrap failed");
228 if (outbuf
.length
> size
)
231 memcpy (output
, outbuf
.value
, outbuf
.length
);
233 /* The real packet size is stored in the data, so we don't need to
234 remember outbuf.length. */
236 gss_release_buffer (&stat_min
, &outbuf
);
243 /* Wrap data using GSSAPI. */
245 cvs_gssapi_wrap_output (void *fnclosure
, const char *input
, char *output
,
246 size_t size
, size_t *translated
)
248 struct cvs_gssapi_wrap_data
*gd
= fnclosure
;
249 gss_buffer_desc inbuf
, outbuf
;
253 inbuf
.value
= (void *)input
;
257 conf_req
= cvs_gssapi_encrypt
;
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");
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)
273 memcpy (output
, outbuf
.value
, outbuf
.length
);
275 *translated
= outbuf
.length
;
277 gss_release_buffer (&stat_min
, &outbuf
);
284 /* Create a GSSAPI wrapping buffer. We use a packetizing buffer with
285 GSSAPI wrapping routines. */
287 cvs_gssapi_wrap_buffer_initialize (struct buffer
*buf
, int input
,
288 gss_ctx_id_t gcontext
,
289 void (*memory
) ( struct buffer
* ))
291 struct cvs_gssapi_wrap_data
*gd
;
293 gd
= xmalloc (sizeof *gd
);
294 gd
->gcontext
= gcontext
;
296 return packetizing_buffer_initialize (buf
,
297 input
? cvs_gssapi_wrap_input
300 : cvs_gssapi_wrap_output
,
307 initialize_gssapi_buffers (struct buffer
**to_server_p
,
308 struct buffer
**from_server_p
)
310 *to_server_p
= cvs_gssapi_wrap_buffer_initialize (*to_server_p
, 0,
313 *from_server_p
= cvs_gssapi_wrap_buffer_initialize (*from_server_p
, 1,
316 #endif /* CLIENT_SUPPORT || SERVER_SUPPORT */