krb5.c
Go to the documentation of this file.
1 /* GSSAPI/krb5 support for FTP - loosely based on old krb4.c
2  *
3  * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
4  * (Royal Institute of Technology, Stockholm, Sweden).
5  * Copyright (c) 2004 - 2016 Daniel Stenberg
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in the
17  * documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of the Institute nor the names of its contributors
20  * may be used to endorse or promote products derived from this software
21  * without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE. */
34 
35 #include "curl_setup.h"
36 
37 #if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_FTP)
38 
39 #ifdef HAVE_NETDB_H
40 #include <netdb.h>
41 #endif
42 
43 #include "urldata.h"
44 #include "curl_base64.h"
45 #include "ftp.h"
46 #include "curl_gssapi.h"
47 #include "sendf.h"
48 #include "curl_sec.h"
49 #include "warnless.h"
50 
51 /* The last 3 #include files should be in this order */
52 #include "curl_printf.h"
53 #include "curl_memory.h"
54 #include "memdebug.h"
55 
56 static int
57 krb5_init(void *app_data)
58 {
59  gss_ctx_id_t *context = app_data;
60  /* Make sure our context is initialized for krb5_end. */
61  *context = GSS_C_NO_CONTEXT;
62  return 0;
63 }
64 
65 static int
66 krb5_check_prot(void *app_data, int level)
67 {
68  (void)app_data; /* unused */
69  if(level == PROT_CONFIDENTIAL)
70  return -1;
71  return 0;
72 }
73 
74 static int
75 krb5_decode(void *app_data, void *buf, int len,
76  int level UNUSED_PARAM,
77  struct connectdata *conn UNUSED_PARAM)
78 {
79  gss_ctx_id_t *context = app_data;
80  OM_uint32 maj, min;
81  gss_buffer_desc enc, dec;
82 
83  (void)level;
84  (void)conn;
85 
86  enc.value = buf;
87  enc.length = len;
88  maj = gss_unseal(&min, *context, &enc, &dec, NULL, NULL);
89  if(maj != GSS_S_COMPLETE) {
90  if(len >= 4)
91  strcpy(buf, "599 ");
92  return -1;
93  }
94 
95  memcpy(buf, dec.value, dec.length);
96  len = curlx_uztosi(dec.length);
97  gss_release_buffer(&min, &dec);
98 
99  return len;
100 }
101 
102 static int
103 krb5_overhead(void *app_data, int level, int len)
104 {
105  /* no arguments are used */
106  (void)app_data;
107  (void)level;
108  (void)len;
109  return 0;
110 }
111 
112 static int
113 krb5_encode(void *app_data, const void *from, int length, int level, void **to)
114 {
115  gss_ctx_id_t *context = app_data;
116  gss_buffer_desc dec, enc;
117  OM_uint32 maj, min;
118  int state;
119  int len;
120 
121  /* NOTE that the cast is safe, neither of the krb5, gnu gss and heimdal
122  * libraries modify the input buffer in gss_seal()
123  */
124  dec.value = (void *)from;
125  dec.length = length;
126  maj = gss_seal(&min, *context,
127  level == PROT_PRIVATE,
129  &dec, &state, &enc);
130 
131  if(maj != GSS_S_COMPLETE)
132  return -1;
133 
134  /* malloc a new buffer, in case gss_release_buffer doesn't work as
135  expected */
136  *to = malloc(enc.length);
137  if(!*to)
138  return -1;
139  memcpy(*to, enc.value, enc.length);
140  len = curlx_uztosi(enc.length);
141  gss_release_buffer(&min, &enc);
142  return len;
143 }
144 
145 static int
146 krb5_auth(void *app_data, struct connectdata *conn)
147 {
148  int ret = AUTH_OK;
149  char *p;
150  const char *host = conn->host.name;
151  ssize_t nread;
152  curl_socklen_t l = sizeof(conn->local_addr);
153  struct Curl_easy *data = conn->data;
155  const char *service = data->set.str[STRING_SERVICE_NAME] ?
156  data->set.str[STRING_SERVICE_NAME] :
157  "ftp";
158  const char *srv_host = "host";
159  gss_buffer_desc input_buffer, output_buffer, _gssresp, *gssresp;
160  OM_uint32 maj, min;
161  gss_name_t gssname;
162  gss_ctx_id_t *context = app_data;
163  struct gss_channel_bindings_struct chan;
164  size_t base64_sz = 0;
165  struct sockaddr_in **remote_addr =
166  (struct sockaddr_in **)&conn->ip_addr->ai_addr;
167  char *stringp;
168 
169  if(getsockname(conn->sock[FIRSTSOCKET],
170  (struct sockaddr *)&conn->local_addr, &l) < 0)
171  perror("getsockname()");
172 
173  chan.initiator_addrtype = GSS_C_AF_INET;
174  chan.initiator_address.length = l - 4;
175  chan.initiator_address.value = &conn->local_addr.sin_addr.s_addr;
176  chan.acceptor_addrtype = GSS_C_AF_INET;
177  chan.acceptor_address.length = l - 4;
178  chan.acceptor_address.value = &(*remote_addr)->sin_addr.s_addr;
179  chan.application_data.length = 0;
180  chan.application_data.value = NULL;
181 
182  /* this loop will execute twice (once for service, once for host) */
183  for(;;) {
184  /* this really shouldn't be repeated here, but can't help it */
185  if(service == srv_host) {
186  result = Curl_ftpsend(conn, "AUTH GSSAPI");
187  if(result)
188  return -2;
189 
190  if(Curl_GetFTPResponse(&nread, conn, NULL))
191  return -1;
192 
193  if(data->state.buffer[0] != '3')
194  return -1;
195  }
196 
197  stringp = aprintf("%s@%s", service, host);
198  if(!stringp)
199  return -2;
200 
201  input_buffer.value = stringp;
202  input_buffer.length = strlen(stringp);
203  maj = gss_import_name(&min, &input_buffer, GSS_C_NT_HOSTBASED_SERVICE,
204  &gssname);
205  free(stringp);
206  if(maj != GSS_S_COMPLETE) {
207  gss_release_name(&min, &gssname);
208  if(service == srv_host) {
209  Curl_failf(data, "Error importing service name %s@%s", service, host);
210  return AUTH_ERROR;
211  }
212  service = srv_host;
213  continue;
214  }
215  /* We pass NULL as |output_name_type| to avoid a leak. */
216  gss_display_name(&min, gssname, &output_buffer, NULL);
217  Curl_infof(data, "Trying against %s\n", output_buffer.value);
218  gssresp = GSS_C_NO_BUFFER;
219  *context = GSS_C_NO_CONTEXT;
220 
221  do {
222  /* Release the buffer at each iteration to avoid leaking: the first time
223  we are releasing the memory from gss_display_name. The last item is
224  taken care by a final gss_release_buffer. */
225  gss_release_buffer(&min, &output_buffer);
226  ret = AUTH_OK;
227  maj = Curl_gss_init_sec_context(data,
228  &min,
229  context,
230  gssname,
231  &Curl_krb5_mech_oid,
232  &chan,
233  gssresp,
234  &output_buffer,
235  TRUE,
236  NULL);
237 
238  if(gssresp) {
239  free(_gssresp.value);
240  gssresp = NULL;
241  }
242 
243  if(GSS_ERROR(maj)) {
244  Curl_infof(data, "Error creating security context\n");
245  ret = AUTH_ERROR;
246  break;
247  }
248 
249  if(output_buffer.length != 0) {
250  char *cmd;
251 
252  result = Curl_base64_encode(data, (char *)output_buffer.value,
253  output_buffer.length, &p, &base64_sz);
254  if(result) {
255  Curl_infof(data, "base64-encoding: %s\n",
256  curl_easy_strerror(result));
257  ret = AUTH_ERROR;
258  break;
259  }
260 
261  cmd = aprintf("ADAT %s", p);
262  if(cmd)
263  result = Curl_ftpsend(conn, cmd);
264  else
265  result = CURLE_OUT_OF_MEMORY;
266 
267  free(p);
268 
269  if(result) {
270  ret = -2;
271  break;
272  }
273 
274  if(Curl_GetFTPResponse(&nread, conn, NULL)) {
275  ret = -1;
276  break;
277  }
278 
279  if(data->state.buffer[0] != '2' && data->state.buffer[0] != '3') {
280  Curl_infof(data, "Server didn't accept auth data\n");
281  ret = AUTH_ERROR;
282  break;
283  }
284 
285  p = data->state.buffer + 4;
286  p = strstr(p, "ADAT=");
287  if(p) {
288  result = Curl_base64_decode(p + 5,
289  (unsigned char **)&_gssresp.value,
290  &_gssresp.length);
291  if(result) {
292  Curl_failf(data, "base64-decoding: %s",
293  curl_easy_strerror(result));
294  ret = AUTH_CONTINUE;
295  break;
296  }
297  }
298 
299  gssresp = &_gssresp;
300  }
301  } while(maj == GSS_S_CONTINUE_NEEDED);
302 
303  gss_release_name(&min, &gssname);
304  gss_release_buffer(&min, &output_buffer);
305 
306  if(gssresp)
307  free(_gssresp.value);
308 
309  if(ret == AUTH_OK || service == srv_host)
310  return ret;
311 
312  service = srv_host;
313  }
314  return ret;
315 }
316 
317 static void krb5_end(void *app_data)
318 {
319  OM_uint32 min;
320  gss_ctx_id_t *context = app_data;
321  if(*context != GSS_C_NO_CONTEXT) {
322 #ifdef DEBUGBUILD
323  OM_uint32 maj =
324 #endif
325  gss_delete_sec_context(&min, context, GSS_C_NO_BUFFER);
326  DEBUGASSERT(maj == GSS_S_COMPLETE);
327  }
328 }
329 
330 struct Curl_sec_client_mech Curl_krb5_client_mech = {
331  "GSSAPI",
332  sizeof(gss_ctx_id_t),
333  krb5_init,
334  krb5_auth,
335  krb5_end,
336  krb5_check_prot,
337  krb5_overhead,
338  krb5_encode,
339  krb5_decode
340 };
341 
342 #endif /* HAVE_GSSAPI && !CURL_DISABLE_FTP */
#define free(ptr)
Definition: curl_memory.h:130
#define UNUSED_PARAM
Definition: curl_setup.h:660
OM_uint32 gss_release_buffer(OM_uint32 *min, gss_buffer_t buffer)
Definition: stub_gssapi.c:239
#define state(x, y)
Definition: ftp.c:100
#define GSS_C_AF_INET
Definition: stub_gssapi.h:48
CURLcode Curl_ftpsend(struct connectdata *conn, const char *cmd)
Definition: ftp.c:3989
#define GSS_S_CONTINUE_NEEDED
Definition: stub_gssapi.h:34
struct UserDefined set
Definition: urldata.h:1762
CURLcode Curl_base64_decode(const char *src, unsigned char **outptr, size_t *outlen)
Definition: base64.c:100
#define FIRSTSOCKET
Definition: urldata.h:487
CURLcode Curl_base64_encode(struct Curl_easy *data, const char *inputbuff, size_t insize, char **outptr, size_t *outlen)
Definition: base64.c:291
#define DEBUGASSERT(x)
CURLcode
Definition: curl.h:454
CURLcode Curl_GetFTPResponse(ssize_t *nreadp, struct connectdata *conn, int *ftpcode)
Definition: ftp.c:624
#define AUTH_ERROR
Definition: curl_sec.h:39
OM_uint32 gss_display_name(OM_uint32 *min, gss_const_name_t input_name, gss_buffer_t output_name_buffer, gss_OID *output_name_type)
Definition: stub_gssapi.c:335
struct hostname host
Definition: urldata.h:833
int curlx_uztosi(size_t uznum)
Definition: warnless.c:203
#define gss_seal
Definition: setup-vms.h:367
#define malloc(size)
Definition: curl_memory.h:124
struct gss_name_t_desc_struct * gss_name_t
Definition: stub_gssapi.h:85
void Curl_failf(struct Curl_easy *data, const char *fmt,...)
Definition: sendf.c:242
char * name
Definition: urldata.h:444
UNITTEST_START int result
Definition: unit1304.c:49
const char ** p
Definition: unit1394.c:76
static srvr_sockaddr_union_t from
Definition: tftpd.c:197
struct sockaddr * ai_addr
Definition: curl_addrinfo.h:58
size_t len
Definition: curl_sasl.c:55
#define GSS_C_NT_HOSTBASED_SERVICE
Definition: stub_gssapi.h:65
memcpy(filename, filename1, strlen(filename1))
Curl_addrinfo * ip_addr
Definition: urldata.h:821
CURL_TYPEOF_CURL_SOCKLEN_T curl_socklen_t
Definition: system.h:414
#define AUTH_CONTINUE
Definition: curl_sec.h:38
char * buffer
Definition: urldata.h:1253
double min(double a, double b)
void Curl_infof(struct Curl_easy *data, const char *fmt,...)
Definition: sendf.c:224
uint32_t OM_uint32
Definition: stub_gssapi.h:67
#define GSS_ERROR(status)
Definition: stub_gssapi.h:30
#define gss_unseal
Definition: setup-vms.h:368
#define GSS_C_NO_BUFFER
Definition: stub_gssapi.h:39
struct UrlState state
Definition: urldata.h:1769
#define aprintf
Definition: curl_printf.h:46
OM_uint32 gss_release_name(OM_uint32 *min, gss_name_t *input_name)
Definition: stub_gssapi.c:280
#define gss_delete_sec_context
Definition: setup-os400.h:183
#define gss_import_name
Definition: setup-os400.h:152
#define ssize_t
Definition: config-win32.h:382
curl_socket_t sock[2]
Definition: urldata.h:876
char buf[3]
Definition: unit1398.c:32
#define AUTH_OK
Definition: curl_sec.h:37
char * str[STRING_LAST]
Definition: urldata.h:1663
#define TRUE
#define GSS_C_QOP_DEFAULT
Definition: stub_gssapi.h:36
CURL_EXTERN const char * curl_easy_strerror(CURLcode)
Definition: strerror.c:57
struct gss_ctx_id_t_desc_struct * gss_ctx_id_t
Definition: stub_gssapi.h:81
Definition: debug.c:29
#define GSS_S_COMPLETE
Definition: stub_gssapi.h:32
#define GSS_C_NO_CONTEXT
Definition: stub_gssapi.h:40
struct Curl_easy * data
Definition: urldata.h:791


rc_tagdetect_client
Author(s): Monika Florek-Jasinska , Raphael Schaller
autogenerated on Sat Feb 13 2021 03:42:15