Annotation of embedaddon/curl/lib/krb5.c, revision 1.1.1.1

1.1       misho       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 - 2019 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_unwrap(&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_wrap()
                    123:    */
                    124:   dec.value = (void *)from;
                    125:   dec.length = length;
                    126:   maj = gss_wrap(&min, *context,
                    127:                  level == PROT_PRIVATE,
                    128:                  GSS_C_QOP_DEFAULT,
                    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;
                    154:   CURLcode result;
                    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:         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:         free(cmd);
                    269: 
                    270:         if(result) {
                    271:           ret = -2;
                    272:           break;
                    273:         }
                    274: 
                    275:         if(Curl_GetFTPResponse(&nread, conn, NULL)) {
                    276:           ret = -1;
                    277:           break;
                    278:         }
                    279: 
                    280:         if(data->state.buffer[0] != '2' && data->state.buffer[0] != '3') {
                    281:           Curl_infof(data, "Server didn't accept auth data\n");
                    282:           ret = AUTH_ERROR;
                    283:           break;
                    284:         }
                    285: 
                    286:         _gssresp.value = NULL; /* make sure it is initialized */
                    287:         p = data->state.buffer + 4;
                    288:         p = strstr(p, "ADAT=");
                    289:         if(p) {
                    290:           result = Curl_base64_decode(p + 5,
                    291:                                       (unsigned char **)&_gssresp.value,
                    292:                                       &_gssresp.length);
                    293:           if(result) {
                    294:             failf(data, "base64-decoding: %s", curl_easy_strerror(result));
                    295:             ret = AUTH_CONTINUE;
                    296:             break;
                    297:           }
                    298:         }
                    299: 
                    300:         gssresp = &_gssresp;
                    301:       }
                    302:     } while(maj == GSS_S_CONTINUE_NEEDED);
                    303: 
                    304:     gss_release_name(&min, &gssname);
                    305:     gss_release_buffer(&min, &output_buffer);
                    306: 
                    307:     if(gssresp)
                    308:       free(_gssresp.value);
                    309: 
                    310:     if(ret == AUTH_OK || service == srv_host)
                    311:       return ret;
                    312: 
                    313:     service = srv_host;
                    314:   }
                    315:   return ret;
                    316: }
                    317: 
                    318: static void krb5_end(void *app_data)
                    319: {
                    320:     OM_uint32 min;
                    321:     gss_ctx_id_t *context = app_data;
                    322:     if(*context != GSS_C_NO_CONTEXT) {
                    323:       OM_uint32 maj = gss_delete_sec_context(&min, context, GSS_C_NO_BUFFER);
                    324:       (void)maj;
                    325:       DEBUGASSERT(maj == GSS_S_COMPLETE);
                    326:     }
                    327: }
                    328: 
                    329: struct Curl_sec_client_mech Curl_krb5_client_mech = {
                    330:     "GSSAPI",
                    331:     sizeof(gss_ctx_id_t),
                    332:     krb5_init,
                    333:     krb5_auth,
                    334:     krb5_end,
                    335:     krb5_check_prot,
                    336:     krb5_overhead,
                    337:     krb5_encode,
                    338:     krb5_decode
                    339: };
                    340: 
                    341: #endif /* HAVE_GSSAPI && !CURL_DISABLE_FTP */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>