/* * 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); }
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); }
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); }
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 ); }
/* * 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); }
/* * 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); }
/* * 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); }
/* * 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); }
/* * 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); }
/* * 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; }
/* * 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); }
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");; }
/* * 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); }
/* * 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); }
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); }
/* * 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); }
/* * 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 */ }
/* * 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); }
/* * 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); }