Esempio n. 1
0
/*
 * Callback for bsd_stream_read_sync
 */
static void
stream_read_sync_callback(
    void *	s)
{
    struct sec_stream *bs = s;
    ssize_t n;

    assert(bs != NULL);

    auth_debug(1, _("bsd: stream_read_callback_sync: fd %d\n"), bs->fd);

    /*
     * Remove the event first, in case they reschedule it in the callback.
     */
    bsd_stream_read_cancel(bs);
    do {
	n = read(bs->fd, bs->databuf, sizeof(bs->databuf));
    } while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN)));
    if (n < 0)
        security_stream_seterror(&bs->secstr, "%s", strerror(errno));
    bs->len = n;
    sync_pktlen = bs->len;
    sync_pkt = malloc(sync_pktlen);
    memcpy(sync_pkt, bs->databuf, sync_pktlen);
}
Esempio n. 2
0
static int
k5_decrypt(
    void *cookie,
    void *buf,
    ssize_t buflen,
    void **decbuf,
    ssize_t *decbuflen)
{
    struct tcp_conn *rc = cookie;
    gss_buffer_desc enctok;
    gss_buffer_desc dectok;
    OM_uint32 maj_stat, min_stat;
    int conf_state, qop_state;

    if (rc->conf_fn && rc->conf_fn("kencrypt", rc->datap)) {
	auth_debug(1, _("krb5: k5_decrypt: enter\n"));
	if (rc->auth == 1) {
	    enctok.length = buflen;
	    enctok.value  = buf;    

	    auth_debug(1, _("krb5: k5_decrypt: decrypting %zu bytes\n"), enctok.length);

	    assert(rc->gss_context != GSS_C_NO_CONTEXT);
	    maj_stat = gss_unseal(&min_stat, rc->gss_context, &enctok, &dectok,
			      &conf_state, &qop_state);
	    if (maj_stat != (OM_uint32)GSS_S_COMPLETE) {
		auth_debug(1, _("krb5 decrypt error from %s: %s\n"),
			   rc->hostname, gss_error(maj_stat, min_stat));
		return (-1);
	    }
	    auth_debug(1, _("krb5: k5_decrypt: give %zu bytes\n"),
		       dectok.length);
	    *decbuf = dectok.value;
	    *decbuflen = dectok.length;
	} else {
	    *decbuf = buf;
	    *decbuflen = buflen;
	}
	auth_debug(1, _("krb5: k5_decrypt: exit\n"));
    } else {
	*decbuf = buf;
	*decbuflen = buflen;
    }
    return (0);
}
Esempio n. 3
0
static int
k5_encrypt(
    void *cookie,
    void *buf,
    ssize_t buflen,
    void **encbuf,
    ssize_t *encbuflen)
{
    struct tcp_conn *rc = cookie;
    gss_buffer_desc dectok;
    gss_buffer_desc enctok;
    OM_uint32 maj_stat, min_stat;
    int conf_state;

    if (rc->conf_fn && rc->conf_fn("kencrypt", rc->datap)) {
	auth_debug(1, _("krb5: k5_encrypt: enter %p\n"), rc);

	dectok.length = buflen;
	dectok.value  = buf;    

	if (rc->auth == 1) {
	    assert(rc->gss_context != GSS_C_NO_CONTEXT);
	    maj_stat = gss_seal(&min_stat, rc->gss_context, 1,
			        GSS_C_QOP_DEFAULT, &dectok, &conf_state,
				&enctok);
	    if (maj_stat != (OM_uint32)GSS_S_COMPLETE || conf_state == 0) {
		auth_debug(1, _("krb5 encrypt error to %s: %s\n"),
			   rc->hostname, gss_error(maj_stat, min_stat));
		return (-1);
	    }
	    auth_debug(1, _("krb5: k5_encrypt: give %zu bytes\n"),
		       enctok.length);
	    *encbuf = enctok.value;
	    *encbuflen = enctok.length;
	} else {
	    *encbuf = buf;
	    *encbuflen = buflen;
	}
	auth_debug(1, _("krb5: k5_encrypt: exit\n"));
    }
    return (0);
}
Esempio n. 4
0
File: debug.c Progetto: zixia/wmail
void auth_debug_login( int level, const char *fmt, ... ) {

	va_list ap;

	// logging severity
	if( level > auth_debug_login_level )
		return;

	fprintf( stderr, "LOGIN: DEBUG: ip=[%s], ", getenv("TCPREMOTEIP") );
	va_start( ap, fmt );
	auth_debug( fmt, ap );
	va_end( ap );
}
Esempio n. 5
0
/*
 * Frees a handle allocated by the above
 */
static void
bsd_close(
    void *	cookie)
{
    struct sec_handle *bh = cookie;

    if(bh->proto_handle == NULL) {
	return;
    }

    auth_debug(1, _("bsd: close handle '%s'\n"), bh->proto_handle);

    udp_recvpkt_cancel(bh);
    if(bh->next) {
	bh->next->prev = bh->prev;
    }
    else {
	if (!not_init6 && netfd6.bh_last == bh)
	    netfd6.bh_last = bh->prev;
	else
	    netfd4.bh_last = bh->prev;
    }
    if(bh->prev) {
	bh->prev->next = bh->next;
    }
    else {
	if (!not_init6 && netfd6.bh_first == bh)
	    netfd6.bh_first = bh->next;
	else
	    netfd4.bh_first = bh->next;
    }

    amfree(bh->proto_handle);
    amfree(bh->hostname);
    amfree(bh);
}
Esempio n. 6
0
/*
 * Negotiate a krb5 gss context from the server end.
 */
static int
gss_server(
    struct tcp_conn *rc)
{
    OM_uint32 maj_stat, min_stat, ret_flags;
    gss_buffer_desc send_tok, recv_tok, AA;
    gss_OID doid;
    gss_name_t gss_name;
    gss_cred_id_t gss_creds;
    char *p, *realm, *msg;
    int rval = -1;
    int rvalue;
    char errbuf[256];
    char *errmsg = NULL;

    auth_debug(1, "gss_server\n");

    assert(rc != NULL);

    /*
     * We need to be root while in gss_acquire_cred() to read the host key
     * out of the default keytab.  We also need to be root in
     * gss_accept_context() thanks to the replay cache code.
     */
    if (!set_root_privs(0)) {
	g_snprintf(errbuf, SIZEOF(errbuf),
	    _("can't take root privileges to read krb5 host key: %s"), strerror(errno));
	goto out;
    }

    rc->gss_context = GSS_C_NO_CONTEXT;
    send_tok.value = vstralloc("host/", myhostname, NULL);
    send_tok.length = strlen(send_tok.value) + 1;
    for (p = send_tok.value; *p != '\0'; p++) {
	if (isupper((int)*p))
	    *p = tolower(*p);
    }
    maj_stat = gss_import_name(&min_stat, &send_tok, GSS_C_NULL_OID,
	&gss_name);
    if (maj_stat != (OM_uint32)GSS_S_COMPLETE) {
	set_root_privs(0);
	g_snprintf(errbuf, SIZEOF(errbuf),
	    _("can't import name %s: %s"), (char *)send_tok.value,
	    gss_error(maj_stat, min_stat));
	amfree(send_tok.value);
	goto out;
    }
    amfree(send_tok.value);

    maj_stat = gss_display_name(&min_stat, gss_name, &AA, &doid);
    dbprintf(_("gss_name %s\n"), (char *)AA.value);
    maj_stat = gss_acquire_cred(&min_stat, gss_name, 0,
	GSS_C_NULL_OID_SET, GSS_C_ACCEPT, &gss_creds, NULL, NULL);
    if (maj_stat != (OM_uint32)GSS_S_COMPLETE) {
	g_snprintf(errbuf, SIZEOF(errbuf),
	    _("can't acquire creds for host key host/%s: %s"), myhostname,
	    gss_error(maj_stat, min_stat));
	gss_release_name(&min_stat, &gss_name);
	set_root_privs(0);
	goto out;
    }
    gss_release_name(&min_stat, &gss_name);

    for (recv_tok.length = 0;;) {
	recv_tok.value = NULL;
        rvalue = tcpm_recv_token(rc, rc->read, &rc->handle, &rc->errmsg,
				 /* (void *) is to avoid type-punning warning */
				 (char **)(void *)&recv_tok.value,
				 (ssize_t *)&recv_tok.length, 60);
	if (rvalue <= 0) {
	    if (rvalue < 0) {
		g_snprintf(errbuf, SIZEOF(errbuf),
		    _("recv error in gss loop: %s"), rc->errmsg);
		amfree(rc->errmsg);
	    } else
		g_snprintf(errbuf, SIZEOF(errbuf), _("EOF in gss loop"));
	    goto out;
	}

	maj_stat = gss_accept_sec_context(&min_stat, &rc->gss_context,
	    gss_creds, &recv_tok, GSS_C_NO_CHANNEL_BINDINGS,
	    &gss_name, &doid, &send_tok, &ret_flags, NULL, NULL);

	if (maj_stat != (OM_uint32)GSS_S_COMPLETE &&
	    maj_stat != (OM_uint32)GSS_S_CONTINUE_NEEDED) {
	    g_snprintf(errbuf, SIZEOF(errbuf),
		_("error accepting context: %s"), gss_error(maj_stat, min_stat));
	    amfree(recv_tok.value);
	    goto out;
	}
	amfree(recv_tok.value);

	if (send_tok.length != 0 && tcpm_send_token(rc, rc->write, 0, &errmsg, send_tok.value, send_tok.length) < 0) {
	    strncpy(errbuf, rc->errmsg, SIZEOF(errbuf) - 1);
	    errbuf[SIZEOF(errbuf) - 1] = '\0';
	    amfree(rc->errmsg);
	    gss_release_buffer(&min_stat, &send_tok);
	    goto out;
	}
	gss_release_buffer(&min_stat, &send_tok);


	/*
	 * If we need to get more from the client, then register for
	 * more packets.
	 */
	if (maj_stat != (OM_uint32)GSS_S_CONTINUE_NEEDED)
	    break;
    }

    maj_stat = gss_display_name(&min_stat, gss_name, &send_tok, &doid);
    if (maj_stat != (OM_uint32)GSS_S_COMPLETE) {
	g_snprintf(errbuf, SIZEOF(errbuf),
	    _("can't display gss name: %s"), gss_error(maj_stat, min_stat));
	gss_release_name(&min_stat, &gss_name);
	goto out;
    }
    gss_release_name(&min_stat, &gss_name);

    /* get rid of the realm */
    if ((p = strchr(send_tok.value, '@')) == NULL) {
	g_snprintf(errbuf, SIZEOF(errbuf),
	    _("malformed gss name: %s"), (char *)send_tok.value);
	amfree(send_tok.value);
	goto out;
    }
    *p = '\0';
    realm = ++p;

    /* 
     * If the principal doesn't match, complain
     */
    if ((msg = krb5_checkuser(rc->hostname, send_tok.value, realm)) != NULL) {
	g_snprintf(errbuf, SIZEOF(errbuf),
	    _("access not allowed from %s: %s"), (char *)send_tok.value, msg);
	amfree(send_tok.value);
	goto out;
    }
    amfree(send_tok.value);

    rval = 0;
out:
    set_root_privs(0);
    if (rval != 0) {
	rc->errmsg = stralloc(errbuf);
    } else {
	rc->auth = 1;
    }
    auth_debug(1, _("gss_server returning %d\n"), rval);
    return (rval);
}
Esempio n. 7
0
/*

 * Negotiate a krb5 gss context from the client end.
 */
static int
gss_client(
    struct sec_handle *rh)
{
    struct sec_stream *rs = rh->rs;
    struct tcp_conn *rc = rs->rc;
    gss_buffer_desc send_tok, recv_tok, AA;
    gss_OID doid;
    OM_uint32 maj_stat, min_stat;
    unsigned int ret_flags;
    int rval = -1;
    int rvalue;
    gss_name_t gss_name;
    char *errmsg = NULL;

    auth_debug(1, "gss_client\n");

    send_tok.value = vstralloc("host/", rs->rc->hostname, NULL);
    send_tok.length = strlen(send_tok.value) + 1;
    maj_stat = gss_import_name(&min_stat, &send_tok, GSS_C_NULL_OID,
	&gss_name);
    if (maj_stat != (OM_uint32)GSS_S_COMPLETE) {
	security_seterror(&rh->sech, _("can't import name %s: %s"),
	    (char *)send_tok.value, gss_error(maj_stat, min_stat));
	amfree(send_tok.value);
	return (-1);
    }
    amfree(send_tok.value);
    rc->gss_context = GSS_C_NO_CONTEXT;
    maj_stat = gss_display_name(&min_stat, gss_name, &AA, &doid);
    dbprintf(_("gss_name %s\n"), (char *)AA.value);

    /*
     * Perform the context-establishement loop.
     *
     * Every generated token is stored in send_tok which is then
     * transmitted to the server; every received token is stored in
     * recv_tok (empty on the first pass) to be processed by
     * the next call to gss_init_sec_context.
     * 
     * GSS-API guarantees that send_tok's length will be non-zero
     * if and only if the server is expecting another token from us,
     * and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if
     * and only if the server has another token to send us.
     */

    recv_tok.value = NULL;
    for (recv_tok.length = 0;;) {
	min_stat = 0;
	maj_stat = gss_init_sec_context(&min_stat,
	    GSS_C_NO_CREDENTIAL,
	    &rc->gss_context,
	    gss_name,
	    GSS_C_NULL_OID,
	    (OM_uint32)GSS_C_MUTUAL_FLAG|GSS_C_REPLAY_FLAG,
	    0, NULL,	/* no channel bindings */
	    (recv_tok.length == 0 ? GSS_C_NO_BUFFER : &recv_tok),
	    NULL,	/* ignore mech type */
	    &send_tok,
	    &ret_flags,
	    NULL);	/* ignore time_rec */

	if (recv_tok.length != 0) {
	    amfree(recv_tok.value);
	    recv_tok.length = 0;
	}
	if (maj_stat != (OM_uint32)GSS_S_COMPLETE && maj_stat != (OM_uint32)GSS_S_CONTINUE_NEEDED) {
	    security_seterror(&rh->sech,
		_("error getting gss context: %s %s"),
		gss_error(maj_stat, min_stat), (char *)send_tok.value);
	    goto done;
	}

	/*
	 * Send back the response
	 */
	if (send_tok.length != 0 && tcpm_send_token(rc, rc->write, rs->handle, &errmsg, send_tok.value, send_tok.length) < 0) {
	    security_seterror(&rh->sech, "%s", rc->errmsg);
	    gss_release_buffer(&min_stat, &send_tok);
	    goto done;
	}
	gss_release_buffer(&min_stat, &send_tok);

	/*
	 * If we need to continue, then register for more packets
	 */
	if (maj_stat != (OM_uint32)GSS_S_CONTINUE_NEEDED)
	    break;

        rvalue = tcpm_recv_token(rc, rc->read, &rc->handle, &rc->errmsg,
				 (void *)&recv_tok.value,
				 (ssize_t *)&recv_tok.length, 60);
	if (rvalue <= 0) {
	    if (rvalue < 0)
		security_seterror(&rh->sech,
		    _("recv error in gss loop: %s"), rc->errmsg);
	    else
		security_seterror(&rh->sech, _("EOF in gss loop"));
	    goto done;
	}
    }

    rval = 0;
    rc->auth = 1;
done:
    gss_release_name(&min_stat, &gss_name);
    return (rval);
}
Esempio n. 8
0
/*
 * krb5 version of a security handle allocator.  Logically sets
 * up a network "connection".
 */
static void
krb5_connect(
    const char *hostname,
    char *	(*conf_fn)(char *, void *),
    void	(*fn)(void *, security_handle_t *, security_status_t),
    void *	arg,
    void *	datap)
{
    struct sec_handle *rh;
    int result;
    char *canonname;

    assert(fn != NULL);
    assert(hostname != NULL);

    auth_debug(1, "krb5: krb5_connect: %s\n", hostname);

    krb5_init();

    rh = alloc(sizeof(*rh));
    security_handleinit(&rh->sech, &krb5_security_driver);
    rh->hostname = NULL;
    rh->rs = NULL;
    rh->ev_timeout = NULL;
    rh->rc = NULL;

    result = resolve_hostname(hostname, 0, NULL, &canonname);
    if(result != 0) {
	dbprintf(_("resolve_hostname(%s): %s\n"), hostname, gai_strerror(result));
	security_seterror(&rh->sech, _("resolve_hostname(%s): %s\n"), hostname,
			  gai_strerror(result));
	(*fn)(arg, &rh->sech, S_ERROR);
	return;
    }
    if (canonname == NULL) {
	dbprintf(_("resolve_hostname(%s) did not return a canonical name\n"), hostname);
	security_seterror(&rh->sech,
	        _("resolve_hostname(%s) did not return a canonical name\n"), hostname);
	(*fn)(arg, &rh->sech, S_ERROR);
       return;
    }

    rh->hostname = canonname;        /* will be replaced */
    canonname = NULL; /* steal reference */
    rh->rs = tcpma_stream_client(rh, newhandle++);
    rh->rc->conf_fn = conf_fn;
    rh->rc->datap = datap;
    rh->rc->recv_security_ok = NULL;
    rh->rc->prefix_packet = NULL;

    if (rh->rs == NULL)
	goto error;

    amfree(rh->hostname);
    rh->hostname = stralloc(rh->rs->rc->hostname);

#ifdef AMANDA_KEYTAB
    keytab_name = AMANDA_KEYTAB;
#else
    if(conf_fn) {
        keytab_name = conf_fn("krb5keytab", datap);
    }
#endif
#ifdef AMANDA_PRINCIPAL
    principal_name = AMANDA_PRINCIPAL;
#else
    if(conf_fn) {
        principal_name = conf_fn("krb5principal", datap);
    }
#endif

    /*
     * We need to open a new connection.
     *
     * XXX need to eventually limit number of outgoing connections here.
     */
    if(rh->rc->read == -1) {
	if (runkrb5(rh) < 0)
	    goto error;
	rh->rc->refcnt++;
    }

    /*
     * The socket will be opened async so hosts that are down won't
     * block everything.  We need to register a write event
     * so we will know when the socket comes alive.
     *
     * Overload rh->rs->ev_read to provide a write event handle.
     * We also register a timeout.
     */
    rh->fn.connect = fn;
    rh->arg = arg;
    rh->rs->ev_read = event_register((event_id_t)(rh->rs->rc->write),
	EV_WRITEFD, sec_connect_callback, rh);
    rh->ev_timeout = event_register(CONNECT_TIMEOUT, EV_TIME,
	sec_connect_timeout, rh);

    amfree(canonname);
    return;

error:
    amfree(canonname);
    (*fn)(arg, &rh->sech, S_ERROR);
}
Esempio n. 9
0
/*
 * bsdtcp version of a security handle allocator.  Logically sets
 * up a network "connection".
 */
static void
bsdtcp_connect(
    const char *hostname,
    char *	(*conf_fn)(char *, void *),
    void	(*fn)(void *, security_handle_t *, security_status_t),
    void *	arg,
    void *	datap)
{
    struct sec_handle *rh;
    int result;
    char *canonname;
    char *service;
    char *src_ip;
    in_port_t port;
    struct addrinfo *res = NULL;

    assert(fn != NULL);
    assert(hostname != NULL);
    (void)conf_fn;	/* Quiet unused parameter warning */
    (void)datap;	/* Quiet unused parameter warning */

    auth_debug(1, _("bsdtcp: bsdtcp_connect: %s\n"), hostname);

    rh = g_new0(struct sec_handle, 1);
    security_handleinit(&rh->sech, &bsdtcp_security_driver);
    rh->dle_hostname = g_strdup(hostname);
    rh->hostname = NULL;
    rh->rs = NULL;
    rh->ev_timeout = NULL;
    rh->rc = NULL;

    result = resolve_hostname(hostname, SOCK_STREAM, &res, &canonname);
    if(result != 0) {
        dbprintf(_("resolve_hostname(%s): %s\n"), hostname, gai_strerror(result));
        security_seterror(&rh->sech, _("resolve_hostname(%s): %s"), hostname,
                          gai_strerror(result));
        (*fn)(arg, &rh->sech, S_ERROR);
        if (res) freeaddrinfo(res);
        return;
    }
    if (canonname == NULL) {
        dbprintf(_("resolve_hostname(%s) did not return a canonical name\n"), hostname);
        security_seterror(&rh->sech,
                          _("resolve_hostname(%s) did not return a canonical name"), hostname);
        (*fn)(arg, &rh->sech, S_ERROR);
        if (res) freeaddrinfo(res);
        return;
    }

    rh->hostname = canonname;	/* will be replaced */
    canonname = NULL; /* steal reference */
    rh->rs = tcpma_stream_client(rh, newhandle++);
    if (rh->rc == NULL)
        goto error;

    rh->rc->recv_security_ok = &bsd_recv_security_ok;
    rh->rc->prefix_packet = &bsd_prefix_packet;
    rh->rc->need_priv_port = 1;

    if (rh->rs == NULL)
        goto error;

    amfree(rh->hostname);
    rh->hostname = g_strdup(rh->rs->rc->hostname);

    if (conf_fn) {
        service = conf_fn("client_port", datap);
        if (!service || strlen(service) <= 1)
            service = AMANDA_SERVICE_NAME;
        src_ip = conf_fn("src_ip", datap);
    } else {
        service = AMANDA_SERVICE_NAME;
        src_ip = NULL;
    }
    port = find_port_for_service(service, "tcp");
    if (port == 0) {
        security_seterror(&rh->sech, _("%s/tcp unknown protocol"), service);
        goto error;
    }

    /*
     * We need to open a new connection.
     *
     * XXX need to eventually limit number of outgoing connections here.
     */
    rh->res = res;
    rh->next_res = res;
    rh->src_ip = src_ip;
    rh->port = port;
    if(rh->rc->read == -1) {
        int result = -1;
        while (rh->next_res) {
            result = runbsdtcp(rh, rh->src_ip, rh->port);
            if (result >=0 )
                break;
        }
        if (result < 0)
            goto error;
        rh->rc->refcnt++;
    }

    /*
     * The socket will be opened async so hosts that are down won't
     * block everything.  We need to register a write event
     * so we will know when the socket comes alive.
     *
     * Overload rh->rs->ev_read to provide a write event handle.
     * We also register a timeout.
     */
    rh->fn.connect = &bsdtcp_fn_connect;
    rh->arg = rh;
    rh->connect_callback = fn;
    rh->connect_arg = arg;
    g_mutex_lock(security_mutex);
    rh->rs->rc->ev_write = event_create((event_id_t)(rh->rs->rc->write),
                                        EV_WRITEFD, sec_connect_callback, rh);
    rh->ev_timeout = event_create(CONNECT_TIMEOUT, EV_TIME,
                                  sec_connect_timeout, rh);
    event_activate(rh->rs->rc->ev_write);
    event_activate(rh->ev_timeout);
    g_mutex_unlock(security_mutex);

    return;

error:
    if (res) {
        freeaddrinfo(res);
    }
    rh->res = NULL;
    rh->next_res = NULL;
    (*fn)(arg, &rh->sech, S_ERROR);
}
Esempio n. 10
0
/*
 * Open a ssl connection to the host listed in rc->hostname
 * Returns negative on error, with an errmsg in rc->errmsg.
 */
static int
runssl(
    struct sec_handle *	rh,
    in_port_t port,
    char *src_ip,
    char *ssl_fingerprint_file,
    char *ssl_cert_file,
    char *ssl_key_file,
    char *ssl_ca_cert_file,
    char *ssl_cipher_list,
    int   ssl_check_certificate_host)
{
    int		     my_socket;
    in_port_t	     my_port;
    struct tcp_conn *rc = rh->rc;
    int              err;
    X509            *remote_cert;
    sockaddr_union   sin;
    socklen_t_equiv  len;

    if (!ssl_key_file) {
	security_seterror(&rh->sech, _("ssl-key-file must be set"));
	return -1;
    }

    if (!ssl_cert_file) {
	security_seterror(&rh->sech, _("ssl-cert-file must be set"));
	return -1;
    }

    my_socket = stream_client(src_ip,
				  rc->hostname,
				  port,
				  STREAM_BUFSIZE,
				  STREAM_BUFSIZE,
				  &my_port,
				  0);

    if(my_socket < 0) {
	security_seterror(&rh->sech,
	    "%s", strerror(errno));

	return -1;
    }

    rc->read = rc->write = my_socket;

    len = sizeof(sin);
    if (getpeername(my_socket, (struct sockaddr *)&sin, &len) < 0) {
	security_seterror(&rh->sech, _("getpeername returned: %s\n"), strerror(errno));
	return -1;
    }
    copy_sockaddr(&rc->peer, &sin);

    init_ssl();

    /* Create an SSL_CTX structure */
    rc->ctx = SSL_CTX_new(SSLv3_client_method());
    if (!rc->ctx) {
	security_seterror(&rh->sech, "%s",
			  ERR_error_string(ERR_get_error(), NULL));
	return -1;
    }
    SSL_CTX_set_mode(rc->ctx, SSL_MODE_AUTO_RETRY);

    if (ssl_cipher_list) {
	g_debug("Set ssl_cipher_list to %s", ssl_cipher_list);
	if (SSL_CTX_set_cipher_list(rc->ctx, ssl_cipher_list) == 0) {
	    security_seterror(&rh->sech, "%s",
		              ERR_error_string(ERR_get_error(), NULL));
	    return -1;
	}
    }

    /* Load the private-key corresponding to the remote certificate */
    g_debug("Loading ssl-key-file private-key %s", ssl_key_file);
    if (SSL_CTX_use_PrivateKey_file(rc->ctx, ssl_key_file,
				    SSL_FILETYPE_PEM) <= 0) {
	security_seterror(&rh->sech, "%s",
			  ERR_error_string(ERR_get_error(), NULL));
	return -1;
    }

    /* Load the me certificate into the SSL_CTX structure */
    g_debug("Loading ssl-cert-file certificate %s", ssl_cert_file);
    if (SSL_CTX_use_certificate_file(rc->ctx, ssl_cert_file,
				     SSL_FILETYPE_PEM) <= 0) {
	security_seterror(&rh->sech, "%s",
			  ERR_error_string(ERR_get_error(), NULL));
	return -1;
    }

    /* Check if the remote certificate and private-key matches */
    if (ssl_cert_file) {
	if (!SSL_CTX_check_private_key(rc->ctx)) {
	security_seterror(&rh->sech,
		_("Private key does not match the certificate public key"));
	return -1;
	}
    }

    if (ssl_ca_cert_file) {
        /* Load the RSA CA certificate into the SSL_CTX structure */
        /* This will allow this remote to verify the me's     */
        /* certificate.                                           */
	g_debug("Loading ssl-ca-cert-file ca %s", ssl_ca_cert_file);
        if (!SSL_CTX_load_verify_locations(rc->ctx, ssl_ca_cert_file, NULL)) {
	    security_seterror(&rh->sech, "%s",
			      ERR_error_string(ERR_get_error(), NULL));
	    return -1;
        }
    } else {
	g_debug(_("no ssl-ca-cert-file defined"));
    }

    /* Set flag in context to require peer (me) certificate */
    /* verification */
    if (ssl_ca_cert_file) {
	g_debug("Enabling certification verification");
	SSL_CTX_set_verify(rc->ctx, SSL_VERIFY_PEER, NULL);
	SSL_CTX_set_verify_depth(rc->ctx, 1);
    } else {
	g_debug("Not enabling certification verification");
    }

    /* ----------------------------------------------- */
    rc->ssl = SSL_new(rc->ctx);
    if (!rc->ssl) {
	security_seterror(&rh->sech, _("SSL_new failed: %s"),
			  ERR_error_string(ERR_get_error(), NULL));
	return -1;
    }
    SSL_set_connect_state(rc->ssl);

    /* Assign the socket into the SSL structure (SSL and socket without BIO) */
    SSL_set_fd(rc->ssl, my_socket);

    /* Perform SSL Handshake on the SSL remote */
    err = SSL_connect(rc->ssl);
    if (err == -1) {
	security_seterror(&rh->sech, _("SSL_connect failed: %s"),
			  ERR_error_string(ERR_get_error(), NULL));
	return -1;
    }

    /* Get the me's certificate (optional) */
    remote_cert = SSL_get_peer_certificate(rc->ssl);

    if (remote_cert == NULL) {
	security_seterror(&rh->sech, _("server have no certificate"));
	return -1;
    } else {
        char *str;

        str = X509_NAME_oneline(X509_get_subject_name(remote_cert), 0, 0);
        auth_debug(1, _("\t subject: %s\n"), str);
        amfree (str);

        str = X509_NAME_oneline(X509_get_issuer_name(remote_cert), 0, 0);
        auth_debug(1, _("\t issuer: %s\n"), str);
        amfree(str);

	if (ssl_check_certificate_host) {
	    int   loc = -1;
	    char *errmsg = NULL;
	    X509_NAME *x509_name = X509_get_subject_name(remote_cert);

	    loc = X509_NAME_get_index_by_NID(x509_name, NID_commonName, loc);
	    if (loc != -1) {
		X509_NAME_ENTRY *x509_entry = X509_NAME_get_entry(x509_name, loc);
		ASN1_STRING *asn1_string = X509_NAME_ENTRY_get_data(x509_entry);
		char *cert_hostname =  (char *)ASN1_STRING_data(asn1_string);
		auth_debug(1, "common_name: %s\n", cert_hostname);

		if (check_name_give_sockaddr((char*)cert_hostname,
				 (struct sockaddr *)&rc->peer, &errmsg) < 0) {
		    security_seterror(&rh->sech,
		       _("Common name of certicate (%s) doesn't resolv to IP (%s): %s"),
		       cert_hostname, str_sockaddr(&rc->peer), errmsg);
		    amfree(errmsg);
		    return -1;
		}
		auth_debug(1,
		         _("Certificate common name (%s) resolve to IP (%s)\n"),
			 cert_hostname, str_sockaddr(&rc->peer));
	    } else {
		security_seterror(&rh->sech,
				  _("Certificate have no common name"));
		g_debug("Certificate have no common name");
		return -1;
	    }
	}

/*
	if (ssl_dir) {
	    if (!ssl_fingerprint_file || ssl_fingerprint_file == '\0') {
		struct stat  statbuf;
		ssl_fingerprint_file = g_strdup_printf("%s/remote/%s/fingerprint", ssl_dir, cert_hostname);
		if (stat(ssl_fingerprint_file, &statbuf) == -1) {
		    g_free(ssl_fingerprint_file);
		    ssl_fingerprint_file = NULL;
		}
	    }
	}
*/

	if (ssl_fingerprint_file) {
            g_debug(_("run_ssl: Loading ssl-fingerprint-file %s"), ssl_fingerprint_file);
	    str = validate_fingerprints(remote_cert, ssl_fingerprint_file);
	    if (str) {
		security_seterror(&rh->sech, "%s", str);
		amfree(str);
		return -1;
	    }
	}
	X509_free (remote_cert);
    }

    g_debug(_("SSL_cipher: %s"), SSL_get_cipher(rc->ssl));

    return 0;
}
Esempio n. 11
0
/*
 * Setup to handle new incoming connections
 */
static void
ssl_accept(
    const struct security_driver *driver,
    char *	(*conf_fn)(char *, void *),
    int		in,
    int		out,
    void	(*fn)(security_handle_t *, pkt_t *),
    void       *datap)
{
    sockaddr_union sin;
    socklen_t_equiv len = sizeof(struct sockaddr);
    struct tcp_conn *rc;
    char hostname[NI_MAXHOST];
    int result;
    char *errmsg = NULL;
    int   err;
    X509 *remote_cert;
    char *str;
    X509_NAME *x509_name;
    char *cert_hostname;
    SSL_CTX            *ctx;
    SSL                *ssl;
    int loc;
    char *ssl_dir = getconf_str(CNF_SSL_DIR);
    char *ssl_fingerprint_file = conf_fn("ssl_fingerprint_file", datap);
    char *ssl_cert_file        = conf_fn("ssl_cert_file", datap);
    char *ssl_key_file         = conf_fn("ssl_key_file", datap);
    char *ssl_ca_cert_file     = conf_fn("ssl_ca_cert_file", datap);
    char *ssl_cipher_list      = conf_fn("ssl_cipher_list", datap);
    int   ssl_check_host       = atoi(conf_fn("ssl_check_host", datap));
    int   ssl_check_certificate_host = atoi(conf_fn("ssl_check_certificate_host", datap));

    if (getpeername(in, (struct sockaddr *)&sin, &len) < 0) {
	g_debug(_("getpeername returned: %s"), strerror(errno));
	return;
    }
    if ((result = getnameinfo((struct sockaddr *)&sin, len,
			      hostname, NI_MAXHOST, NULL, 0, 0) != 0)) {
	g_debug(_("getnameinfo failed: %s"),
		  gai_strerror(result));
	return;
    }

    if (ssl_check_host && check_name_give_sockaddr(hostname,
				 (struct sockaddr *)&sin, &errmsg) < 0) {
	amfree(errmsg);
	return;
    }

    if (ssl_dir) {
	if (!ssl_cert_file || ssl_cert_file == '\0') {
	    ssl_cert_file = g_strdup_printf("%s/me/crt.pem", ssl_dir);
	}
	if (!ssl_key_file || ssl_key_file == '\0') {
	    ssl_key_file = g_strdup_printf("%s/me/private/key.pem", ssl_dir);
	}
	if (!ssl_ca_cert_file || ssl_ca_cert_file == '\0') {
	    ssl_ca_cert_file = g_strdup_printf("%s/CA/crt.pem", ssl_dir);
	}
    }

    if (!ssl_cert_file) {
	g_debug(_("ssl-cert-file must be set in amanda-remote.conf"));
	return;
    }

    if (!ssl_key_file) {
	g_debug(_("ssl-key-file must be set in amanda-remote.conf"));
	return;
    }

    if (!ssl_ca_cert_file) {
	g_debug(_("ssl_ca_cert_file must be set in amanda-remote.conf"));
	return;
    }

    len = sizeof(sin);
    init_ssl();

    /* Create a SSL_CTX structure */
    ctx = SSL_CTX_new(SSLv3_server_method());
    if (!ctx) {
	g_debug(_("SSL_CTX_new failed: %s"),
		 ERR_error_string(ERR_get_error(), NULL));
	return;
    }
    SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);

    if (ssl_cipher_list) {
	g_debug("Set ssl_cipher_list to %s", ssl_cipher_list);
	if (SSL_CTX_set_cipher_list(ctx, ssl_cipher_list) == 0) {
	    g_debug(_("SSL_CTX_set_cipher_list failed: %s"),
		     ERR_error_string(ERR_get_error(), NULL));
	    return;
	}
    }

    /* Load the me certificate into the SSL_CTX structure */
    g_debug(_("Loading ssl-cert-file certificate %s"), ssl_cert_file);
    if (SSL_CTX_use_certificate_file(ctx, ssl_cert_file,
				     SSL_FILETYPE_PEM) <= 0) {
	g_debug(_("Load ssl-cert-file failed: %s"),
		 ERR_error_string(ERR_get_error(), NULL));
	return;
    }

    /* Load the private-key corresponding to the me certificate */
    g_debug(_("Loading ssl-key-file private-key %s"), ssl_key_file);
    if (SSL_CTX_use_PrivateKey_file(ctx, ssl_key_file,
				    SSL_FILETYPE_PEM) <= 0) {
	g_debug(_("Load ssl-key-file failed: %s"),
		 ERR_error_string(ERR_get_error(), NULL));
	return;
    }

    if (ssl_ca_cert_file) {
        /* Load the RSA CA certificate into the SSL_CTX structure */
	g_debug(_("Loading ssl-ca-cert-file ca certificate %s"),
		 ssl_ca_cert_file);
        if (!SSL_CTX_load_verify_locations(ctx, ssl_ca_cert_file, NULL)) {
	    g_debug(_("Load ssl-ca-cert-file failed: %s"),
		     ERR_error_string(ERR_get_error(), NULL));
	    return;
        }

	/* Set to require peer (remote) certificate verification */
	SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);

	/* Set the verification depth to 1 */
	SSL_CTX_set_verify_depth(ctx,1);
    }

    ssl = SSL_new(ctx);
    if (!ssl) {
	g_debug(_("SSL_new failed: %s"),
		 ERR_error_string(ERR_get_error(), NULL));
	return;
    }
    SSL_set_accept_state(ssl);

    /* Assign the socket into the SSL structure (SSL and socket without BIO) */
    SSL_set_fd(ssl, in);

    /* Perform SSL Handshake on the SSL me */
    err = SSL_accept(ssl);
    if (err == -1) {
	g_debug(_("SSL_accept failed: %s"),
		 ERR_error_string(ERR_get_error(), NULL));
	return;
    }

    /* Get the me's certificate (optional) */
    remote_cert = SSL_get_peer_certificate (ssl);

    if (remote_cert == NULL) {
	g_debug(_("remote doesn't sent a certificate"));
	return;
    }

    x509_name = X509_get_subject_name(remote_cert);
    str = X509_NAME_oneline(X509_get_subject_name(remote_cert), 0, 0);
    auth_debug(1, _("\t subject: %s\n"), str);
    amfree (str);

    str = X509_NAME_oneline(X509_get_issuer_name(remote_cert), 0, 0);
    auth_debug(1, _("\t issuer: %s\n"), str);
    amfree(str);

    loc = -1;
    loc = X509_NAME_get_index_by_NID(x509_name, NID_commonName, loc);
    if (loc != -1) {
	X509_NAME_ENTRY *x509_entry = X509_NAME_get_entry(x509_name, loc);
	ASN1_STRING *asn1_string = X509_NAME_ENTRY_get_data(x509_entry);
	cert_hostname = (char *)ASN1_STRING_data(asn1_string);
	auth_debug(1, "common_name: %s\n", cert_hostname);

	if (ssl_check_certificate_host &&
	    check_name_give_sockaddr((char*)cert_hostname,
			 (struct sockaddr *)&sin, &errmsg) < 0) {
	    g_debug("Common name of certicate (%s) doesn't resolv to IP (%s)", cert_hostname, str_sockaddr(&sin));
	    amfree(errmsg);
	    X509_free(remote_cert);
	    return;
	}
    } else {
	g_debug("Certificate have no common name");
	X509_free(remote_cert);
	return;
    }

    if (ssl_dir) {
	if (!ssl_fingerprint_file || ssl_fingerprint_file == '\0') {
	    struct stat  statbuf;
	    ssl_fingerprint_file = g_strdup_printf("%s/remote/%s/fingerprint", ssl_dir, cert_hostname);
	    if (stat(ssl_fingerprint_file, &statbuf) == -1) {
		g_free(ssl_fingerprint_file);
		ssl_fingerprint_file = NULL;
	    }
	}
    }

    if (ssl_fingerprint_file) {
        g_debug(_("Loading ssl-fingerprint-file %s"), ssl_fingerprint_file);
	str = validate_fingerprints(remote_cert, ssl_fingerprint_file);
	if (str) {
	    g_debug("%s", str);
	    amfree(str);
	    X509_free(remote_cert);
	    return;
	}
    }
    X509_free(remote_cert);

    rc = sec_tcp_conn_get(hostname, 0);
    rc->recv_security_ok = &bsd_recv_security_ok;
    rc->prefix_packet = &bsd_prefix_packet;
    rc->need_priv_port = 0;
    copy_sockaddr(&rc->peer, &sin);
    rc->read = in;
    rc->write = out;
    rc->accept_fn = fn;
    rc->driver = driver;
    rc->conf_fn = conf_fn;
    rc->datap = datap;
    rc->ctx = ctx;
    rc->ssl = ssl;
    strncpy(rc->hostname, cert_hostname, sizeof(rc->hostname)-1);

    g_debug(_("SSL_cipher: %s"), SSL_get_cipher(rc->ssl));

    sec_tcp_conn_read(rc);
}
Esempio n. 12
0
static char *
validate_fingerprints(
    X509 *cert,
    char *ssl_fingerprint_file)
{
    FILE *fingers;
    char fingerprint[32768];
    char *errmsg = NULL;

    unsigned char  md5[EVP_MAX_MD_SIZE + 1];
    unsigned int   len_md5;
    const EVP_MD  *evp_md5;
    char *md5_fingerprint;
    unsigned char  sha1[EVP_MAX_MD_SIZE + 1];
    unsigned int   len_sha1;
    const EVP_MD  *evp_sha1;
    char *sha1_fingerprint;
    char *fp;
    unsigned int   i;

    const char *md5_const  = "MD5 Fingerprint=";
    const char *sha1_const = "SHA1 Fingerprint=";
    const size_t md5_const_len = strlen(md5_const);
    const size_t sha1_const_len = strlen(sha1_const);

    if (ssl_fingerprint_file == NULL) {
	g_debug("No fingerprint_file set");
	return NULL;
    }

    evp_md5 = EVP_get_digestbyname("MD5");
    if (!evp_md5) {
	auth_debug(1, _("EVP_get_digestbyname(MD5) failed"));
    }
    if (!X509_digest(cert, evp_md5, md5, &len_md5)) {
	auth_debug(1, _("cannot get MD5 digest"));
    }

    md5_fingerprint  = malloc(len_md5*3 + 1);
    fp = md5_fingerprint;
    for (i=0; i < len_md5; i++) {
	snprintf(fp, 4, "%02X:", (unsigned) md5[i]);
	fp+=3;
    }
    /* remove latest ':' */
    fp --;
    *fp = '\0';
    auth_debug(1, _("md5: %s\n"), md5_fingerprint);

    evp_sha1 = EVP_get_digestbyname("SHA1");
    if (!evp_sha1) {
	auth_debug(1, _("EVP_get_digestbyname(SHA1) failed"));
    }
    if (!X509_digest(cert, evp_sha1, sha1, &len_sha1)) {
	auth_debug(1, _("cannot get SHA1 digest"));
    }

    sha1_fingerprint  = malloc(len_sha1*3 + 1);
    fp = sha1_fingerprint;
    for (i=0; i < len_sha1; i++) {
	snprintf(fp, 4, "%02X:", (unsigned) sha1[i]);
	fp+=3;
    }
    /* remove latest ':' */
    fp --;
    *fp = '\0';
    auth_debug(1, _("sha1: %s\n"), sha1_fingerprint);

    fingers = fopen(ssl_fingerprint_file, "r");
    if (!fingers) {
	errmsg = g_strdup_printf("Failed open of %s: %s",
				 ssl_fingerprint_file, strerror(errno));
	g_debug("%s", errmsg);
	g_free(md5_fingerprint);
	g_free(sha1_fingerprint);
	return errmsg;
    }

    while (fgets(fingerprint, 32768, fingers) != NULL) {
	int len = strlen(fingerprint)-1;
	if (len > 0 && fingerprint[len] == '\n')
	    fingerprint[len] = '\0';
	if (strncmp(md5_const, fingerprint, md5_const_len) == 0) {
	    if (strcmp(md5_fingerprint, fingerprint+md5_const_len) == 0) {
		g_debug("MD5 fingerprint '%s' match", md5_fingerprint);
		g_free(md5_fingerprint);
		g_free(sha1_fingerprint);
		fclose(fingers);
		return NULL;
	    }
	} else if (strncmp(sha1_const, fingerprint, sha1_const_len) == 0) {
	    if (strcmp(sha1_fingerprint, fingerprint+sha1_const_len) == 0) {
		g_debug("SHA1 fingerprint '%s' match", sha1_fingerprint);
		g_free(md5_fingerprint);
		g_free(sha1_fingerprint);
		fclose(fingers);
		return NULL;
	    }
	}
	auth_debug(1, _("Fingerprint '%s' doesn't match\n"), fingerprint);
    }
    g_free(md5_fingerprint);
    g_free(sha1_fingerprint);
    fclose(fingers);
    return g_strdup_printf("No fingerprint match");;
}
Esempio n. 13
0
/*
 * ssl version of a security handle allocator.  Logically sets
 * up a network "connection".
 */
static void
ssl_connect(
    const char *hostname,
    char *	(*conf_fn)(char *, void *),
    void	(*fn)(void *, security_handle_t *, security_status_t),
    void *	arg,
    void *	datap)
{
    struct sec_handle *rh;
    int result;
    char *canonname;
    char *service;
    in_port_t port;
    char *src_ip = NULL;
    char *ssl_dir = NULL;
    char *ssl_fingerprint_file = NULL;
    char *ssl_cert_file = NULL;
    char *ssl_key_file = NULL;
    char *ssl_ca_cert_file = NULL;
    char *ssl_cipher_list = NULL;
    int   ssl_check_certificate_host = 1;

    assert(fn != NULL);
    assert(hostname != NULL);

    auth_debug(1, _("ssl: ssl_connect: %s\n"), hostname);

    rh = g_new0(struct sec_handle, 1);
    security_handleinit(&rh->sech, &ssl_security_driver);
    rh->hostname = NULL;
    rh->rs = NULL;
    rh->ev_timeout = NULL;
    rh->rc = NULL;

    result = resolve_hostname(hostname, 0, NULL, &canonname);
    if(result != 0) {
	g_debug(_("resolve_hostname(%s): %s"), hostname, gai_strerror(result));
	security_seterror(&rh->sech, _("resolve_hostname(%s): %s\n"), hostname,
			  gai_strerror(result));
	(*fn)(arg, &rh->sech, S_ERROR);
	return;
    }
    if (canonname == NULL) {
	g_debug(_("resolve_hostname(%s) did not return a canonical name"), hostname);
	security_seterror(&rh->sech,
	        _("resolve_hostname(%s) did not return a canonical name\n"), hostname);
	(*fn)(arg, &rh->sech, S_ERROR);
       return;
    }

    rh->hostname = canonname;	/* will be replaced */
    canonname = NULL; /* steal reference */
    rh->rs = tcpma_stream_client(rh, newhandle++);
    rh->rc->recv_security_ok = &bsd_recv_security_ok;
    rh->rc->prefix_packet = &bsd_prefix_packet;
    rh->rc->need_priv_port = 0;

    if (rh->rs == NULL)
	goto error;

    amfree(rh->hostname);
    rh->hostname = g_strdup(rh->rs->rc->hostname);

    ssl_dir = getconf_str(CNF_SSL_DIR);
    if (conf_fn) {
	service = conf_fn("remote_port", datap);
	if (!service || strlen(service) <= 1)
	    service = AMANDA_SERVICE_NAME;
	g_debug("Connecting to service '%s'", service);
	src_ip = conf_fn("src_ip", datap);
	ssl_fingerprint_file = g_strdup(conf_fn("ssl_fingerprint_file", datap));
	ssl_cert_file        = g_strdup(conf_fn("ssl_cert_file", datap));
	ssl_key_file         = g_strdup(conf_fn("ssl_key_file", datap));
	ssl_ca_cert_file     = g_strdup(conf_fn("ssl_ca_cert_file", datap));
	ssl_cipher_list      = conf_fn("ssl_cipher_list", datap);
	ssl_check_certificate_host =
			    atoi(conf_fn("ssl_check_certificate_host", datap));
    } else {
	service = AMANDA_SERVICE_NAME;
    }

    if (ssl_dir) {
	if (!ssl_cert_file || ssl_cert_file == '\0') {
	    ssl_cert_file = g_strdup_printf("%s/me/crt.pem", ssl_dir);
	}
	if (!ssl_key_file || ssl_key_file == '\0') {
	    ssl_key_file = g_strdup_printf("%s/me/private/key.pem", ssl_dir);
	}
	if (!ssl_ca_cert_file || ssl_ca_cert_file == '\0') {
	    ssl_ca_cert_file = g_strdup_printf("%s/CA/crt.pem", ssl_dir);
	}
	if (!ssl_fingerprint_file || ssl_fingerprint_file == '\0') {
	    struct stat  statbuf;
	    ssl_fingerprint_file = g_strdup_printf("%s/remote/%s/fingerprint", ssl_dir, rh->hostname);
	    if (stat(ssl_fingerprint_file, &statbuf) == -1) {
		g_free(ssl_fingerprint_file);
		ssl_fingerprint_file = NULL;
	    }
	}
    }

    port = find_port_for_service(service, "tcp");
    if (port == 0) {
	security_seterror(&rh->sech, _("%s/tcp unknown protocol"), service);
	goto error;
    }

    /*
     * We need to open a new connection.
     */
    if(rh->rc->read == -1) {
	if (runssl(rh, port, src_ip, ssl_fingerprint_file,
                   ssl_cert_file, ssl_key_file,
		   ssl_ca_cert_file, ssl_cipher_list,
		   ssl_check_certificate_host) < 0)
	    goto error;
	rh->rc->refcnt++;
    }

    g_free(ssl_fingerprint_file);
    g_free(ssl_cert_file);
    g_free(ssl_key_file);
    g_free(ssl_ca_cert_file);
    /*
     * The socket will be opened async so hosts that are down won't
     * block everything.  We need to register a write event
     * so we will know when the socket comes alive.
     *
     * Overload rh->rs->ev_read to provide a write event handle.
     * We also register a timeout.
     */
    rh->fn.connect = fn;
    rh->arg = arg;
    rh->rs->ev_read = event_register((event_id_t)(rh->rs->rc->write),
	EV_WRITEFD, sec_connect_callback, rh);
    rh->ev_timeout = event_register(CONNECT_TIMEOUT, EV_TIME,
	sec_connect_timeout, rh);

    return;

error:
    (*fn)(arg, &rh->sech, S_ERROR);
}
Esempio n. 14
0
/*
 * bsdtcp version of a security handle allocator.  Logically sets
 * up a network "connection".
 */
static void
bsdtcp_connect(
    const char *hostname,
    char *	(*conf_fn)(char *, void *),
    void	(*fn)(void *, security_handle_t *, security_status_t),
    void *	arg,
    void *	datap)
{
    struct sec_handle *rh;
    int result;
    char *canonname;
    char *service;
    in_port_t port;

    assert(fn != NULL);
    assert(hostname != NULL);
    (void)conf_fn;	/* Quiet unused parameter warning */
    (void)datap;	/* Quiet unused parameter warning */

    auth_debug(1, _("bsdtcp: bsdtcp_connect: %s\n"), hostname);

    rh = alloc(sizeof(*rh));
    security_handleinit(&rh->sech, &bsdtcp_security_driver);
    rh->hostname = NULL;
    rh->rs = NULL;
    rh->ev_timeout = NULL;
    rh->rc = NULL;

    result = resolve_hostname(hostname, 0, NULL, &canonname);
    if(result != 0) {
	dbprintf(_("resolve_hostname(%s): %s\n"), hostname, gai_strerror(result));
	security_seterror(&rh->sech, _("resolve_hostname(%s): %s\n"), hostname,
			  gai_strerror(result));
	(*fn)(arg, &rh->sech, S_ERROR);
	return;
    }
    if (canonname == NULL) {
	dbprintf(_("resolve_hostname(%s) did not return a canonical name\n"), hostname);
	security_seterror(&rh->sech,
	        _("resolve_hostname(%s) did not return a canonical name\n"), hostname);
	(*fn)(arg, &rh->sech, S_ERROR);
       return;
    }

    rh->hostname = canonname;	/* will be replaced */
    canonname = NULL; /* steal reference */
    rh->rs = tcpma_stream_client(rh, newhandle++);
    rh->rc->recv_security_ok = &bsd_recv_security_ok;
    rh->rc->prefix_packet = &bsd_prefix_packet;

    if (rh->rs == NULL)
	goto error;

    amfree(rh->hostname);
    rh->hostname = stralloc(rh->rs->rc->hostname);

    if (conf_fn) {
	service = conf_fn("client_port", datap);
	if (strlen(service) <= 1)
	    service = "amanda";
    } else {
	service = "amanda";
    }
    port = find_port_for_service(service, "tcp");
    if (port == 0) {
	security_seterror(&rh->sech, _("%s/tcp unknown protocol"), service);
	goto error;
    }

    /*
     * We need to open a new connection.
     *
     * XXX need to eventually limit number of outgoing connections here.
     */
    if(rh->rc->read == -1) {
	if (runbsdtcp(rh, port) < 0)
	    goto error;
	rh->rc->refcnt++;
    }

    /*
     * The socket will be opened async so hosts that are down won't
     * block everything.  We need to register a write event
     * so we will know when the socket comes alive.
     *
     * Overload rh->rs->ev_read to provide a write event handle.
     * We also register a timeout.
     */
    rh->fn.connect = fn;
    rh->arg = arg;
    rh->rs->ev_read = event_register((event_id_t)(rh->rs->rc->write),
	EV_WRITEFD, sec_connect_callback, rh);
    rh->ev_timeout = event_register(CONNECT_TIMEOUT, EV_TIME,
	sec_connect_timeout, rh);

    return;

error:
    (*fn)(arg, &rh->sech, S_ERROR);
}
Esempio n. 15
0
static void
bsd_connect(
    const char *	hostname,
    char *		(*conf_fn)(char *, void *),
    void		(*fn)(void *, security_handle_t *, security_status_t),
    void *		arg,
    void *		datap)
{
    struct sec_handle *bh;
    in_port_t port = 0;
    struct timeval sequence_time;
    int sequence;
    char *handle;
    int result;
    struct addrinfo *res, *res_addr;
    char *canonname;
    int result_bind;
    char *service;

    assert(hostname != NULL);

    (void)conf_fn;	/* Quiet unused parameter warning */
    (void)datap;        /* Quiet unused parameter warning */

    bh = g_new0(struct sec_handle, 1);
    bh->proto_handle=NULL;
    security_handleinit(&bh->sech, &bsd_security_driver);

    result = resolve_hostname(hostname, SOCK_DGRAM, &res, &canonname);
    if(result != 0) {
	dbprintf(_("resolve_hostname(%s): %s\n"), hostname, gai_strerror(result));
	security_seterror(&bh->sech, _("resolve_hostname(%s): %s\n"), hostname,
			  gai_strerror(result));
	(*fn)(arg, &bh->sech, S_ERROR);
	return;
    }
    if (canonname == NULL) {
	dbprintf(_("resolve_hostname(%s) did not return a canonical name\n"), hostname);
	security_seterror(&bh->sech,
	        _("resolve_hostname(%s) did not return a canonical name\n"), hostname);
	(*fn)(arg, &bh->sech, S_ERROR);
	if (res) freeaddrinfo(res);
	return;
    }
    if (res == NULL) {
	dbprintf(_("resolve_hostname(%s): no results\n"), hostname);
	security_seterror(&bh->sech,
	        _("resolve_hostname(%s): no results\n"), hostname);
	(*fn)(arg, &bh->sech, S_ERROR);
	amfree(canonname);
	return;
    }

    for (res_addr = res; res_addr != NULL; res_addr = res_addr->ai_next) {
#ifdef WORKING_IPV6
	/* IPv6 socket already bound */
	if (res_addr->ai_addr->sa_family == AF_INET6 && not_init6 == 0) {
	    break;
	}
	/*
	 * Only init the IPv6 socket once
	 */
	if (res_addr->ai_addr->sa_family == AF_INET6 && not_init6 == 1) {
	    uid_t euid;
	    dgram_zero(&netfd6.dgram);

	    euid = geteuid();
	    set_root_privs(1);
	    result_bind = dgram_bind(&netfd6.dgram,
				     res_addr->ai_addr->sa_family, &port);
	    set_root_privs(0);
	    if (result_bind != 0) {
		continue;
	    }
	    netfd6.handle = NULL;
	    netfd6.pkt.body = NULL;
	    netfd6.recv_security_ok = &bsd_recv_security_ok;
	    netfd6.prefix_packet = &bsd_prefix_packet;
	    /*
	     * We must have a reserved port.  Bomb if we didn't get one.
	     */
	    if (port >= IPPORT_RESERVED) {
		security_seterror(&bh->sech,
		    _("unable to bind to a reserved port (got port %u)"),
		    (unsigned int)port);
		(*fn)(arg, &bh->sech, S_ERROR);
		freeaddrinfo(res);
		amfree(canonname);
		return;
	    }
	    not_init6 = 0;
	    bh->udp = &netfd6;
	    break;
	}
#endif

	/* IPv4 socket already bound */
	if (res_addr->ai_addr->sa_family == AF_INET && not_init4 == 0) {
	    break;
	}

	/*
	 * Only init the IPv4 socket once
	 */
	if (res_addr->ai_addr->sa_family == AF_INET && not_init4 == 1) {
	    uid_t euid;
	    dgram_zero(&netfd4.dgram);

	    euid = geteuid();
	    set_root_privs(1);
	    result_bind = dgram_bind(&netfd4.dgram,
				     res_addr->ai_addr->sa_family, &port);
	    set_root_privs(0);
	    if (result_bind != 0) {
		continue;
	    }
	    netfd4.handle = NULL;
	    netfd4.pkt.body = NULL;
	    netfd4.recv_security_ok = &bsd_recv_security_ok;
	    netfd4.prefix_packet = &bsd_prefix_packet;
	    /*
	     * We must have a reserved port.  Bomb if we didn't get one.
	     */
	    if (port >= IPPORT_RESERVED) {
		security_seterror(&bh->sech,
		    "unable to bind to a reserved port (got port %u)",
		    (unsigned int)port);
		(*fn)(arg, &bh->sech, S_ERROR);
		freeaddrinfo(res);
		amfree(canonname);
		return;
	    }
	    not_init4 = 0;
	    bh->udp = &netfd4;
	    break;
	}
    }

    if (res_addr == NULL) {
	dbprintf(_("Can't bind a socket to connect to %s\n"), hostname);
	security_seterror(&bh->sech,
	        _("Can't bind a socket to connect to %s\n"), hostname);
	(*fn)(arg, &bh->sech, S_ERROR);
	amfree(canonname);
	freeaddrinfo(res);
	return;
    }

#ifdef WORKING_IPV6
    if (res_addr->ai_addr->sa_family == AF_INET6)
	bh->udp = &netfd6;
    else
#endif
	bh->udp = &netfd4;

    auth_debug(1, _("Resolved hostname=%s\n"), canonname);

    if (conf_fn) {
        service = conf_fn("client_port", datap);
        if (!service || strlen(service) <= 1)
            service = "amanda";
    } else {
        service = "amanda";
    }
    port = find_port_for_service(service, "udp");
    if (port == 0) {
        security_seterror(&bh->sech, _("%s/udp unknown protocol"), service);
	(*fn)(arg, &bh->sech, S_ERROR);
        amfree(canonname);
	freeaddrinfo(res);
	return;
    }

    amanda_gettimeofday(&sequence_time);
    sequence = (int)sequence_time.tv_sec ^ (int)sequence_time.tv_usec;
    handle=g_malloc(15);
    g_snprintf(handle, 14, "000-%08x",  (unsigned)newhandle++);
    if (udp_inithandle(bh->udp, bh, canonname,
	(sockaddr_union *)res_addr->ai_addr, port, handle, sequence) < 0) {
	(*fn)(arg, &bh->sech, S_ERROR);
	amfree(bh->hostname);
	amfree(bh);
    }
    else {
	(*fn)(arg, &bh->sech, S_OK);
    }
    amfree(handle);
    amfree(canonname);

    freeaddrinfo(res);
}
Esempio n. 16
0
/*
 * ssh version of a security handle allocator.  Logically sets
 * up a network "connection".
 */
static void
ssh_connect(
    const char *	hostname,
    char *		(*conf_fn)(char *, void *),
    void		(*fn)(void *, security_handle_t *, security_status_t),
    void *		arg,
    void *		datap)
{
    struct sec_handle *rh;
    char *amandad_path=NULL, *client_username=NULL, *ssh_keys=NULL;
    char *client_port = NULL;

    assert(fn != NULL);
    assert(hostname != NULL);

    auth_debug(1, "ssh_connect: %s\n", hostname);

    rh = g_new0(struct sec_handle, 1);
    security_handleinit(&rh->sech, &ssh_security_driver);
    rh->dle_hostname = g_strdup(hostname);
    rh->hostname = NULL;
    rh->rs = NULL;
    rh->ev_timeout = NULL;
    rh->rc = NULL;

    rh->hostname = g_strdup(hostname);
    rh->rs = tcpma_stream_client(rh, newhandle++);
    if (rh->rc == NULL)
	goto error;
    rh->rc->conf_fn = conf_fn;
    rh->rc->datap = datap;

    if (rh->rs == NULL)
	goto error;

    amfree(rh->hostname);
    rh->hostname = g_strdup(rh->rs->rc->hostname);

    /*
     * We need to open a new connection.
     *
     * XXX need to eventually limit number of outgoing connections here.
     */
    if(conf_fn) {
	char *port_str;
	amandad_path    = conf_fn("amandad_path", datap);
	client_username = conf_fn("client_username", datap);
	ssh_keys        = conf_fn("ssh_keys", datap);
	port_str        = conf_fn("client_port", datap);
	if (port_str && strlen(port_str) >= 1) {
	    client_port = port_str;
	}
    }
    if(rh->rc->read == -1) {
	if (runssh(rh->rs->rc, amandad_path, client_username, ssh_keys,
		   client_port) < 0) {
	    security_seterror(&rh->sech, _("can't connect to %s: %s"),
			      hostname, rh->rs->rc->errmsg);
	    goto error;
	}
	rh->rc->refcnt++;
    }

    /*
     * The socket will be opened async so hosts that are down won't
     * block everything.  We need to register a write event
     * so we will know when the socket comes alive.
     *
     * Overload rh->rs->ev_read to provide a write event handle.
     * We also register a timeout.
     */
    rh->fn.connect = fn;
    rh->arg = arg;
    rh->rs->rc->ev_write = event_register((event_id_t)rh->rs->rc->write, EV_WRITEFD,
	sec_connect_callback, rh);
    rh->ev_timeout = event_register((event_id_t)CONNECT_TIMEOUT, EV_TIME,
	sec_connect_timeout, rh);

    return;

error:
    (*fn)(arg, &rh->sech, S_ERROR);
}
Esempio n. 17
0
/*
 * check ~/.k5amandahosts to see if this principal is allowed in.  If it's
 * hardcoded, then we don't check the realm
 */
static char *
krb5_checkuser( char *	host,
    char *	name,
    char *	realm)
{
#ifdef AMANDA_PRINCIPAL
    if(strcmp(name, AMANDA_PRINCIPAL) == 0) {
	return(NULL);
    } else {
	return(vstrallocf(_("does not match compiled in default")));
    }
#else
    struct passwd *pwd;
    char *ptmp;
    char *result = _("generic error");	/* default is to not permit */
    FILE *fp = NULL;
    struct stat sbuf;
    uid_t localuid;
    char *line = NULL;
    char *filehost = NULL, *fileuser = NULL, *filerealm = NULL;

    assert( host != NULL);
    assert( name != NULL);

    if((pwd = getpwnam(CLIENT_LOGIN)) == NULL) {
	result = vstrallocf(_("can not find user %s"), CLIENT_LOGIN);
    }
    localuid = pwd->pw_uid;

#ifdef USE_AMANDAHOSTS
    ptmp = stralloc2(pwd->pw_dir, "/.k5amandahosts");
#else
    ptmp = stralloc2(pwd->pw_dir, "/.k5login");
#endif

    if(!ptmp) {
	result = vstrallocf(_("could not find home directory for %s"), CLIENT_LOGIN);
	goto common_exit;
   }

   /*
    * check to see if the ptmp file does nto exist.
    */
   if(access(ptmp, R_OK) == -1 && errno == ENOENT) {
	/*
	 * in this case we check to see if the principal matches
	 * the destination user mimicing the .k5login functionality.
	 */
	 if(strcmp(name, CLIENT_LOGIN) != 0) {
		result = vstrallocf(_("%s does not match %s"),
			name, CLIENT_LOGIN);
		return result;
	}
	result = NULL;
	goto common_exit;
    }

    auth_debug(1, _("opening ptmp: %s\n"), (ptmp)?ptmp: "NULL!");
    if((fp = fopen(ptmp, "r")) == NULL) {
	result = vstrallocf(_("can not open %s"), ptmp);
	return result;
    }
    auth_debug(1, _("opened ptmp\n"));

    if (fstat(fileno(fp), &sbuf) != 0) {
	result = vstrallocf(_("cannot fstat %s: %s"), ptmp, strerror(errno));
	goto common_exit;
    }

    if (sbuf.st_uid != localuid) {
	result = vstrallocf(_("%s is owned by %ld, should be %ld"),
		ptmp, (long)sbuf.st_uid, (long)localuid);
	goto common_exit;
    }
    if ((sbuf.st_mode & 077) != 0) {
	result = vstrallocf(
	    _("%s: incorrect permissions; file must be accessible only by its owner"), ptmp);
	goto common_exit;
    }

    while ((line = agets(fp)) != NULL) {
	if (line[0] == '\0') {
	    amfree(line);
	    continue;
	}

	/* if there's more than one column, then it's the host */
	if( (filehost = strtok(line, " \t")) == NULL) {
	    amfree(line);
	    continue;
	}

	/*
	 * if there's only one entry, then it's a username and we have
	 * no hostname.  (so the principal is allowed from anywhere.
	 */
	if((fileuser = strtok(NULL, " \t")) == NULL) {
	    fileuser = filehost;
	    filehost = NULL;
	}

	if(filehost && strcmp(filehost, host) != 0) {
	    amfree(line);
	    continue;
	} else {
		auth_debug(1, _("found a host match\n"));
	}

	if( (filerealm = strchr(fileuser, '@')) != NULL) {
	    *filerealm++ = '\0';
	}

	/*
	 * we have a match.  We're going to be a little bit insecure
	 * and indicate that the principal is correct but the realm is
	 * not if that's the case.  Technically we should say nothing
	 * and let the user figure it out, but it's helpful for debugging.
	 * You likely only get this far if you've turned on cross-realm auth
	 * anyway...
	 */
	auth_debug(1, _("comparing %s %s\n"), fileuser, name);
	if(strcmp(fileuser, name) == 0) {
		auth_debug(1, _("found a match!\n"));
		if(realm && filerealm && (strcmp(realm, filerealm)!=0)) {
			amfree(line);
			continue;
		}
		result = NULL;
		amfree(line);
		goto common_exit;
	}
	amfree(line);
    }
    result = vstrallocf(_("no match in %s"), ptmp);

common_exit:
    afclose(fp);
    return(result);
#endif /* AMANDA_PRINCIPAL */
}
Esempio n. 18
0
/*
 * ssh version of a security handle allocator.  Logically sets
 * up a network "connection".
 */
static void
ssh_connect(
    const char *	hostname,
    char *		(*conf_fn)(char *, void *),
    void		(*fn)(void *, security_handle_t *, security_status_t),
    void *		arg,
    void *		datap)
{
    int result;
    struct sec_handle *rh;
    char *amandad_path=NULL, *client_username=NULL, *ssh_keys=NULL;

    assert(fn != NULL);
    assert(hostname != NULL);

    auth_debug(1, "ssh_connect: %s\n", hostname);

    rh = alloc(SIZEOF(*rh));
    security_handleinit(&rh->sech, &ssh_security_driver);
    rh->hostname = NULL;
    rh->rs = NULL;
    rh->ev_timeout = NULL;
    rh->rc = NULL;

    /* get the canonical hostname */
    rh->hostname = NULL;
    if ((result = resolve_hostname(hostname, 0, NULL, &rh->hostname)) != 0
	 || rh->hostname == NULL) {
	security_seterror(&rh->sech,
	    _("ssh_security could not find canonical name for '%s': %s"),
	    hostname, gai_strerror(result));
	(*fn)(arg, &rh->sech, S_ERROR);
	return;
    }
    rh->rs = tcpma_stream_client(rh, newhandle++);
    rh->rc->conf_fn = conf_fn;
    rh->rc->datap = datap;

    if (rh->rs == NULL)
	goto error;

    amfree(rh->hostname);
    rh->hostname = stralloc(rh->rs->rc->hostname);

    /*
     * We need to open a new connection.
     *
     * XXX need to eventually limit number of outgoing connections here.
     */
    if(conf_fn) {
	amandad_path    = conf_fn("amandad_path", datap);
	client_username = conf_fn("client_username", datap);
	ssh_keys        = conf_fn("ssh_keys", datap);
    }
    if(rh->rc->read == -1) {
	if (runssh(rh->rs->rc, amandad_path, client_username, ssh_keys) < 0) {
	    security_seterror(&rh->sech, _("can't connect to %s: %s"),
			      hostname, rh->rs->rc->errmsg);
	    goto error;
	}
	rh->rc->refcnt++;
    }

    /*
     * The socket will be opened async so hosts that are down won't
     * block everything.  We need to register a write event
     * so we will know when the socket comes alive.
     *
     * Overload rh->rs->ev_read to provide a write event handle.
     * We also register a timeout.
     */
    rh->fn.connect = fn;
    rh->arg = arg;
    rh->rs->ev_read = event_register((event_id_t)rh->rs->rc->write, EV_WRITEFD,
	sec_connect_callback, rh);
    rh->ev_timeout = event_register((event_id_t)CONNECT_TIMEOUT, EV_TIME,
	sec_connect_timeout, rh);

    return;

error:
    (*fn)(arg, &rh->sech, S_ERROR);
}
Esempio n. 19
0
/*
 * local version of a security handle allocator.  Logically sets
 * up a network "connection".
 */
static void
local_connect(
    const char *	hostname,
    char *		(*conf_fn)(char *, void *),
    void		(*fn)(void *, security_handle_t *, security_status_t),
    void *		arg,
    void *		datap)
{
    struct sec_handle *rh;
    char *amandad_path=NULL;
    char *client_username=NULL;
    char myhostname[MAX_HOSTNAME_LENGTH+1];

    assert(fn != NULL);
    assert(hostname != NULL);

    auth_debug(1, _("local: local_connect: %s\n"), hostname);

    rh = g_new0(struct sec_handle, 1);
    security_handleinit(&rh->sech, &local_security_driver);
    rh->hostname = NULL;
    rh->rs = NULL;
    rh->ev_timeout = NULL;
    rh->rc = NULL;

    if (gethostname(myhostname, MAX_HOSTNAME_LENGTH) == -1) {
	security_seterror(&rh->sech, _("gethostname failed"));
	(*fn)(arg, &rh->sech, S_ERROR);
	return;
    }
    myhostname[SIZEOF(myhostname)-1] = '\0';

    if (strcmp(hostname, myhostname) != 0 &&
	match("^localhost(\\.localdomain)?$", hostname) == 0) {
	security_seterror(&rh->sech,
	    _("%s: is not local"), hostname);
	(*fn)(arg, &rh->sech, S_ERROR);
	return;
    }
    rh->hostname = stralloc(hostname);
    rh->rs = tcpma_stream_client(rh, newhandle++);

    if (rh->rs == NULL)
	goto error;

    amfree(rh->hostname);
    rh->hostname = stralloc(rh->rs->rc->hostname);

    /*
     * We need to open a new connection.
     *
     * XXX need to eventually limit number of outgoing connections here.
     */
    if(conf_fn) {
	amandad_path    = conf_fn("amandad_path", datap);
	client_username = conf_fn("client_username", datap);
    }
    if(rh->rc->read == -1) {
	if (runlocal(rh->rs->rc, amandad_path, client_username) < 0) {
	    security_seterror(&rh->sech, _("can't connect to %s: %s"),
			      hostname, rh->rs->rc->errmsg);
	    goto error;
	}
	rh->rc->refcnt++;
    }

    /*
     * The socket will be opened async so hosts that are down won't
     * block everything.  We need to register a write event
     * so we will know when the socket comes alive.
     *
     * Overload rh->rs->ev_read to provide a write event handle.
     * We also register a timeout.
     */
    rh->fn.connect = fn;
    rh->arg = arg;
    rh->rs->ev_read = event_register((event_id_t)rh->rs->rc->write, EV_WRITEFD,
	sec_connect_callback, rh);
    rh->ev_timeout = event_register((event_id_t)CONNECT_TIMEOUT, EV_TIME,
	sec_connect_timeout, rh);

    return;

error:
    (*fn)(arg, &rh->sech, S_ERROR);
    amfree(rh->hostname);
}