155682Smarkm/*
255682Smarkm * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska H�gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
555682Smarkm *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
955682Smarkm *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#ifdef FTP_SERVER
3555682Smarkm#include "ftpd_locl.h"
3655682Smarkm#else
3755682Smarkm#include "ftp_locl.h"
3855682Smarkm#endif
3955682Smarkm#include <krb.h>
4055682Smarkm
41178825SdfrRCSID("$Id: krb4.c 17450 2006-05-05 11:11:43Z lha $");
4255682Smarkm
4355682Smarkm#ifdef FTP_SERVER
4455682Smarkm#define LOCAL_ADDR ctrl_addr
4555682Smarkm#define REMOTE_ADDR his_addr
4655682Smarkm#else
4755682Smarkm#define LOCAL_ADDR myctladdr
4855682Smarkm#define REMOTE_ADDR hisctladdr
4955682Smarkm#endif
5055682Smarkm
5155682Smarkmextern struct sockaddr *LOCAL_ADDR, *REMOTE_ADDR;
5255682Smarkm
5355682Smarkmstruct krb4_data {
5455682Smarkm    des_cblock key;
5555682Smarkm    des_key_schedule schedule;
5655682Smarkm    char name[ANAME_SZ];
5755682Smarkm    char instance[INST_SZ];
5855682Smarkm    char realm[REALM_SZ];
5955682Smarkm};
6055682Smarkm
6155682Smarkmstatic int
6255682Smarkmkrb4_check_prot(void *app_data, int level)
6355682Smarkm{
6455682Smarkm    if(level == prot_confidential)
6555682Smarkm	return -1;
6655682Smarkm    return 0;
6755682Smarkm}
6855682Smarkm
6955682Smarkmstatic int
7055682Smarkmkrb4_decode(void *app_data, void *buf, int len, int level)
7155682Smarkm{
7255682Smarkm    MSG_DAT m;
7355682Smarkm    int e;
7455682Smarkm    struct krb4_data *d = app_data;
7555682Smarkm
7655682Smarkm    if(level == prot_safe)
7755682Smarkm	e = krb_rd_safe(buf, len, &d->key,
7855682Smarkm			(struct sockaddr_in *)REMOTE_ADDR,
7955682Smarkm			(struct sockaddr_in *)LOCAL_ADDR, &m);
8055682Smarkm    else
8155682Smarkm	e = krb_rd_priv(buf, len, d->schedule, &d->key,
8255682Smarkm			(struct sockaddr_in *)REMOTE_ADDR,
8355682Smarkm			(struct sockaddr_in *)LOCAL_ADDR, &m);
8455682Smarkm    if(e){
8555682Smarkm	syslog(LOG_ERR, "krb4_decode: %s", krb_get_err_text(e));
8655682Smarkm	return -1;
8755682Smarkm    }
8855682Smarkm    memmove(buf, m.app_data, m.app_length);
8955682Smarkm    return m.app_length;
9055682Smarkm}
9155682Smarkm
9255682Smarkmstatic int
9355682Smarkmkrb4_overhead(void *app_data, int level, int len)
9455682Smarkm{
9555682Smarkm    return 31;
9655682Smarkm}
9755682Smarkm
9855682Smarkmstatic int
9955682Smarkmkrb4_encode(void *app_data, void *from, int length, int level, void **to)
10055682Smarkm{
10155682Smarkm    struct krb4_data *d = app_data;
10255682Smarkm    *to = malloc(length + 31);
10355682Smarkm    if(level == prot_safe)
10455682Smarkm	return krb_mk_safe(from, *to, length, &d->key,
10555682Smarkm			   (struct sockaddr_in *)LOCAL_ADDR,
10655682Smarkm			   (struct sockaddr_in *)REMOTE_ADDR);
10755682Smarkm    else if(level == prot_private)
10855682Smarkm	return krb_mk_priv(from, *to, length, d->schedule, &d->key,
10955682Smarkm			   (struct sockaddr_in *)LOCAL_ADDR,
11055682Smarkm			   (struct sockaddr_in *)REMOTE_ADDR);
11155682Smarkm    else
11255682Smarkm	return -1;
11355682Smarkm}
11455682Smarkm
11555682Smarkm#ifdef FTP_SERVER
11655682Smarkm
11755682Smarkmstatic int
11855682Smarkmkrb4_adat(void *app_data, void *buf, size_t len)
11955682Smarkm{
12055682Smarkm    KTEXT_ST tkt;
12155682Smarkm    AUTH_DAT auth_dat;
12255682Smarkm    char *p;
12355682Smarkm    int kerror;
124178825Sdfr    uint32_t cs;
12555682Smarkm    char msg[35]; /* size of encrypted block */
12655682Smarkm    int tmp_len;
12755682Smarkm    struct krb4_data *d = app_data;
12855682Smarkm    char inst[INST_SZ];
12955682Smarkm    struct sockaddr_in *his_addr_sin = (struct sockaddr_in *)his_addr;
13055682Smarkm
13155682Smarkm    memcpy(tkt.dat, buf, len);
13255682Smarkm    tkt.length = len;
13355682Smarkm
13455682Smarkm    k_getsockinst(0, inst, sizeof(inst));
13555682Smarkm    kerror = krb_rd_req(&tkt, "ftp", inst,
13655682Smarkm			his_addr_sin->sin_addr.s_addr, &auth_dat, "");
13755682Smarkm    if(kerror == RD_AP_UNDEC){
13855682Smarkm	k_getsockinst(0, inst, sizeof(inst));
13955682Smarkm	kerror = krb_rd_req(&tkt, "rcmd", inst,
14055682Smarkm			    his_addr_sin->sin_addr.s_addr, &auth_dat, "");
14155682Smarkm    }
14255682Smarkm
14355682Smarkm    if(kerror){
14455682Smarkm	reply(535, "Error reading request: %s.", krb_get_err_text(kerror));
14555682Smarkm	return -1;
14655682Smarkm    }
14755682Smarkm
14855682Smarkm    memcpy(d->key, auth_dat.session, sizeof(d->key));
14955682Smarkm    des_set_key(&d->key, d->schedule);
15055682Smarkm
15155682Smarkm    strlcpy(d->name, auth_dat.pname, sizeof(d->name));
15255682Smarkm    strlcpy(d->instance, auth_dat.pinst, sizeof(d->instance));
15355682Smarkm    strlcpy(d->realm, auth_dat.prealm, sizeof(d->instance));
15455682Smarkm
15555682Smarkm    cs = auth_dat.checksum + 1;
15655682Smarkm    {
15755682Smarkm	unsigned char tmp[4];
15855682Smarkm	KRB_PUT_INT(cs, tmp, 4, sizeof(tmp));
15955682Smarkm	tmp_len = krb_mk_safe(tmp, msg, 4, &d->key,
16055682Smarkm			      (struct sockaddr_in *)LOCAL_ADDR,
16155682Smarkm			      (struct sockaddr_in *)REMOTE_ADDR);
16255682Smarkm    }
16355682Smarkm    if(tmp_len < 0){
16455682Smarkm	reply(535, "Error creating reply: %s.", strerror(errno));
16555682Smarkm	return -1;
16655682Smarkm    }
16755682Smarkm    len = tmp_len;
16855682Smarkm    if(base64_encode(msg, len, &p) < 0) {
16955682Smarkm	reply(535, "Out of memory base64-encoding.");
17055682Smarkm	return -1;
17155682Smarkm    }
17255682Smarkm    reply(235, "ADAT=%s", p);
17355682Smarkm    sec_complete = 1;
17455682Smarkm    free(p);
17555682Smarkm    return 0;
17655682Smarkm}
17755682Smarkm
17855682Smarkmstatic int
17955682Smarkmkrb4_userok(void *app_data, char *user)
18055682Smarkm{
18155682Smarkm    struct krb4_data *d = app_data;
18255682Smarkm    return krb_kuserok(d->name, d->instance, d->realm, user);
18355682Smarkm}
18455682Smarkm
18555682Smarkmstruct sec_server_mech krb4_server_mech = {
18655682Smarkm    "KERBEROS_V4",
18755682Smarkm    sizeof(struct krb4_data),
18855682Smarkm    NULL, /* init */
18955682Smarkm    NULL, /* end */
19055682Smarkm    krb4_check_prot,
19155682Smarkm    krb4_overhead,
19255682Smarkm    krb4_encode,
19355682Smarkm    krb4_decode,
19455682Smarkm    /* */
19555682Smarkm    NULL,
19655682Smarkm    krb4_adat,
19755682Smarkm    NULL, /* pbsz */
19855682Smarkm    NULL, /* ccc */
19955682Smarkm    krb4_userok
20055682Smarkm};
20155682Smarkm
20255682Smarkm#else /* FTP_SERVER */
20355682Smarkm
20455682Smarkmstatic int
20572445Sassarkrb4_init(void *app_data)
20672445Sassar{
20772445Sassar   return !use_kerberos;
20872445Sassar}
20972445Sassar
21072445Sassarstatic int
21155682Smarkmmk_auth(struct krb4_data *d, KTEXT adat,
21255682Smarkm	char *service, char *host, int checksum)
21355682Smarkm{
21455682Smarkm    int ret;
21555682Smarkm    CREDENTIALS cred;
21655682Smarkm    char sname[SNAME_SZ], inst[INST_SZ], realm[REALM_SZ];
21755682Smarkm
21855682Smarkm    strlcpy(sname, service, sizeof(sname));
21955682Smarkm    strlcpy(inst, krb_get_phost(host), sizeof(inst));
22055682Smarkm    strlcpy(realm, krb_realmofhost(host), sizeof(realm));
22155682Smarkm    ret = krb_mk_req(adat, sname, inst, realm, checksum);
22255682Smarkm    if(ret)
22355682Smarkm	return ret;
22455682Smarkm    strlcpy(sname, service, sizeof(sname));
22555682Smarkm    strlcpy(inst, krb_get_phost(host), sizeof(inst));
22655682Smarkm    strlcpy(realm, krb_realmofhost(host), sizeof(realm));
22755682Smarkm    ret = krb_get_cred(sname, inst, realm, &cred);
22855682Smarkm    memmove(&d->key, &cred.session, sizeof(des_cblock));
22955682Smarkm    des_key_sched(&d->key, d->schedule);
23055682Smarkm    memset(&cred, 0, sizeof(cred));
23155682Smarkm    return ret;
23255682Smarkm}
23355682Smarkm
23455682Smarkmstatic int
23555682Smarkmkrb4_auth(void *app_data, char *host)
23655682Smarkm{
23755682Smarkm    int ret;
23855682Smarkm    char *p;
23955682Smarkm    int len;
24055682Smarkm    KTEXT_ST adat;
24155682Smarkm    MSG_DAT msg_data;
24255682Smarkm    int checksum;
243178825Sdfr    uint32_t cs;
24455682Smarkm    struct krb4_data *d = app_data;
24555682Smarkm    struct sockaddr_in *localaddr  = (struct sockaddr_in *)LOCAL_ADDR;
24655682Smarkm    struct sockaddr_in *remoteaddr = (struct sockaddr_in *)REMOTE_ADDR;
24755682Smarkm
24855682Smarkm    checksum = getpid();
24955682Smarkm    ret = mk_auth(d, &adat, "ftp", host, checksum);
25055682Smarkm    if(ret == KDC_PR_UNKNOWN)
25155682Smarkm	ret = mk_auth(d, &adat, "rcmd", host, checksum);
25255682Smarkm    if(ret){
25355682Smarkm	printf("%s\n", krb_get_err_text(ret));
25455682Smarkm	return AUTH_CONTINUE;
25555682Smarkm    }
25655682Smarkm
25755682Smarkm#ifdef HAVE_KRB_GET_OUR_IP_FOR_REALM
25855682Smarkm    if (krb_get_config_bool("nat_in_use")) {
25955682Smarkm      struct in_addr natAddr;
26055682Smarkm
26155682Smarkm      if (krb_get_our_ip_for_realm(krb_realmofhost(host),
26255682Smarkm				   &natAddr) != KSUCCESS
26355682Smarkm	  && krb_get_our_ip_for_realm(NULL, &natAddr) != KSUCCESS)
26455682Smarkm	printf("Can't get address for realm %s\n",
26555682Smarkm	       krb_realmofhost(host));
26655682Smarkm      else {
26755682Smarkm	if (natAddr.s_addr != localaddr->sin_addr.s_addr) {
26855682Smarkm	  printf("Using NAT IP address (%s) for kerberos 4\n",
26955682Smarkm		 inet_ntoa(natAddr));
27055682Smarkm	  localaddr->sin_addr = natAddr;
27155682Smarkm
27255682Smarkm	  /*
27355682Smarkm	   * This not the best place to do this, but it
27455682Smarkm	   * is here we know that (probably) NAT is in
27555682Smarkm	   * use!
27655682Smarkm	   */
27755682Smarkm
27855682Smarkm	  passivemode = 1;
27955682Smarkm	  printf("Setting: Passive mode on.\n");
28055682Smarkm	}
28155682Smarkm      }
28255682Smarkm    }
28355682Smarkm#endif
28455682Smarkm
28555682Smarkm    printf("Local address is %s\n", inet_ntoa(localaddr->sin_addr));
28655682Smarkm    printf("Remote address is %s\n", inet_ntoa(remoteaddr->sin_addr));
28755682Smarkm
28855682Smarkm   if(base64_encode(adat.dat, adat.length, &p) < 0) {
28955682Smarkm	printf("Out of memory base64-encoding.\n");
29055682Smarkm	return AUTH_CONTINUE;
29155682Smarkm    }
29255682Smarkm    ret = command("ADAT %s", p);
29355682Smarkm    free(p);
29455682Smarkm
29555682Smarkm    if(ret != COMPLETE){
29655682Smarkm	printf("Server didn't accept auth data.\n");
29755682Smarkm	return AUTH_ERROR;
29855682Smarkm    }
29955682Smarkm
30055682Smarkm    p = strstr(reply_string, "ADAT=");
30155682Smarkm    if(!p){
30255682Smarkm	printf("Remote host didn't send adat reply.\n");
30355682Smarkm	return AUTH_ERROR;
30455682Smarkm    }
30555682Smarkm    p += 5;
30655682Smarkm    len = base64_decode(p, adat.dat);
30755682Smarkm    if(len < 0){
30855682Smarkm	printf("Failed to decode base64 from server.\n");
30955682Smarkm	return AUTH_ERROR;
31055682Smarkm    }
31155682Smarkm    adat.length = len;
31255682Smarkm    ret = krb_rd_safe(adat.dat, adat.length, &d->key,
31355682Smarkm		      (struct sockaddr_in *)hisctladdr,
31455682Smarkm		      (struct sockaddr_in *)myctladdr, &msg_data);
31555682Smarkm    if(ret){
31655682Smarkm	printf("Error reading reply from server: %s.\n",
31755682Smarkm	       krb_get_err_text(ret));
31855682Smarkm	return AUTH_ERROR;
31955682Smarkm    }
32055682Smarkm    krb_get_int(msg_data.app_data, &cs, 4, 0);
32155682Smarkm    if(cs - checksum != 1){
32255682Smarkm	printf("Bad checksum returned from server.\n");
32355682Smarkm	return AUTH_ERROR;
32455682Smarkm    }
32555682Smarkm    return AUTH_OK;
32655682Smarkm}
32755682Smarkm
32855682Smarkmstruct sec_client_mech krb4_client_mech = {
32955682Smarkm    "KERBEROS_V4",
33055682Smarkm    sizeof(struct krb4_data),
33172445Sassar    krb4_init, /* init */
33255682Smarkm    krb4_auth,
33355682Smarkm    NULL, /* end */
33455682Smarkm    krb4_check_prot,
33555682Smarkm    krb4_overhead,
33655682Smarkm    krb4_encode,
33755682Smarkm    krb4_decode
33855682Smarkm};
33955682Smarkm
34055682Smarkm#endif /* FTP_SERVER */
341