Sync usage with man page.
[netbsd-mini2440.git] / external / gpl2 / xcvs / dist / src / gssapi-client.c
blobe91ff7649a8b99f1ae373aca998bb2b1daefe3fe
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)
6 any later version.
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. */
14 #ifdef HAVE_CONFIG_H
15 # include <config.h>
16 #endif /* HAVE_CONFIG_H */
18 #include "cvs.h"
19 #include "buffer.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 */
28 #ifdef CLIENT_SUPPORT
29 # include "socket-client.h"
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 */
40 /* Receive a given number of bytes. */
42 static void
43 recv_bytes (int sock, char *buf, int need)
45 while (need > 0)
47 int got;
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));
55 buf += got;
56 need -= got;
62 /* Connect to the server using GSSAPI authentication. */
64 /* FIXME
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.
78 #define BUFSIZE 1024
79 int
80 connect_to_gserver (cvsroot_t *root, int sock, const char *hostname)
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;
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);
97 tok_in.value = buf;
98 gss_import_name (&stat_min, &tok_in, GSS_C_NT_HOSTBASED_SERVICE,
99 &server_name);
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,
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)
115 OM_uint32 message_context;
116 OM_uint32 new_stat_min;
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);
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);
131 if (tok_out.length == 0)
133 tok_in.length = 0;
135 else
137 char cbuf[2];
138 int need;
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)
152 ssize_t got;
153 size_t total;
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)))
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;
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);
179 recv_bytes (sock, buf, need);
180 tok_in.length = need;
183 tok_in.value = buf;
184 tok_in_ptr = &tok_in;
186 while (stat_maj == GSS_S_CONTINUE_NEEDED);
188 return 1;
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
199 routines. */
201 struct cvs_gssapi_wrap_data
203 /* The GSSAPI context. */
204 gss_ctx_id_t gcontext;
209 /* Unwrap data using GSSAPI. */
210 static int
211 cvs_gssapi_wrap_input (void *fnclosure, const char *input, char *output,
212 size_t size)
214 struct cvs_gssapi_wrap_data *gd = fnclosure;
215 gss_buffer_desc inbuf, outbuf;
216 OM_uint32 stat_min;
217 int conf;
219 inbuf.value = (void *)input;
220 inbuf.length = size;
222 if (gss_unwrap (&stat_min, gd->gcontext, &inbuf, &outbuf, &conf, NULL)
223 != GSS_S_COMPLETE)
225 error (1, 0, "gss_unwrap failed");
228 if (outbuf.length > size)
229 abort ();
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);
238 return 0;
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)
248 struct cvs_gssapi_wrap_data *gd = fnclosure;
249 gss_buffer_desc inbuf, outbuf;
250 OM_uint32 stat_min;
251 int conf_req, conf;
253 inbuf.value = (void *)input;
254 inbuf.length = size;
256 #ifdef ENCRYPTION
257 conf_req = cvs_gssapi_encrypt;
258 #else
259 conf_req = 0;
260 #endif
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)
271 abort ();
273 memcpy (output, outbuf.value, outbuf.length);
275 *translated = outbuf.length;
277 gss_release_buffer (&stat_min, &outbuf);
279 return 0;
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 * ))
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
298 : NULL,
299 input ? NULL
300 : cvs_gssapi_wrap_output,
301 gd, memory);
306 void
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,
311 gcontext, NULL);
313 *from_server_p = cvs_gssapi_wrap_buffer_initialize (*from_server_p, 1,
314 gcontext, NULL);
316 #endif /* CLIENT_SUPPORT || SERVER_SUPPORT */