Home | History | Annotate | Line # | Download | only in src
      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
     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
     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
    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
    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 *
    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
    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