diff options
author | Gerald Carter <jerry@samba.org> | 2005-06-08 22:10:34 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 10:57:08 -0500 |
commit | fed660877c16562265327c6093ea645cf4176b5c (patch) | |
tree | e92ae1356542ba095d806bbe1093fa56fbc8ddcc /source3/nsswitch/winbindd_ldap.c | |
parent | 66bb4f03c3466205488f72e4878e8801c5bbb295 (diff) | |
download | samba-fed660877c16562265327c6093ea645cf4176b5c.tar.gz samba-fed660877c16562265327c6093ea645cf4176b5c.tar.bz2 samba-fed660877c16562265327c6093ea645cf4176b5c.zip |
r7415: * big change -- volker's new async winbindd from trunk
(This used to be commit a0ac9a8ffd4af31a0ebc423b4acbb2f043d865b8)
Diffstat (limited to 'source3/nsswitch/winbindd_ldap.c')
-rw-r--r-- | source3/nsswitch/winbindd_ldap.c | 646 |
1 files changed, 646 insertions, 0 deletions
diff --git a/source3/nsswitch/winbindd_ldap.c b/source3/nsswitch/winbindd_ldap.c new file mode 100644 index 0000000000..4eedf0ce9f --- /dev/null +++ b/source3/nsswitch/winbindd_ldap.c @@ -0,0 +1,646 @@ +/* + Unix SMB/CIFS implementation. + + winbind ldap proxy code + + Copyright (C) Volker Lendecke + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "winbindd.h" + +/* This rw-buf api is made to avoid memcpy. For now do that like mad... The + idea is to write into a circular list of buffers where the ideal case is + that a read(2) holds a complete request that is then thrown away + completely. */ + +struct ldap_message_queue { + struct ldap_message_queue *prev, *next; + struct ldap_message *msg; +}; + +struct rw_buffer { + uint8_t *data; + size_t ofs, length; +}; + +struct winbind_ldap_client { + struct winbind_ldap_client *next, *prev; + int sock; + BOOL finished; + struct rw_buffer in_buffer, out_buffer; +}; + +static struct winbind_ldap_client *ldap_clients; + +struct winbind_ldap_server { + struct winbind_ldap_server *next, *prev; + int sock; + BOOL ready; /* Bind successful? */ + BOOL finished; + struct rw_buffer in_buffer, out_buffer; + int messageid; +}; + +static struct winbind_ldap_server *ldap_servers; + +struct pending_ldap_message { + struct pending_ldap_message *next, *prev; + struct ldap_message *msg; /* The message the client sent us */ + int our_msgid; /* The messageid we used */ + struct winbind_ldap_client *client; +}; + +struct pending_ldap_message *pending_messages; + +static BOOL append_to_buf(struct rw_buffer *buf, uint8_t *data, size_t length) +{ + buf->data = SMB_REALLOC(buf->data, buf->length+length); + + if (buf->data == NULL) + return False; + + memcpy(buf->data+buf->length, data, length); + + buf->length += length; + return True; +} + +static BOOL read_into_buf(int fd, struct rw_buffer *buf) +{ + char tmp_buf[1024]; + int len; + + len = read(fd, tmp_buf, sizeof(tmp_buf)); + if (len == 0) + return False; + + return append_to_buf(buf, tmp_buf, len); +} + +static void peek_into_buf(struct rw_buffer *buf, uint8_t **out, + size_t *out_length) +{ + *out = buf->data; + *out_length = buf->length; +} + +static void consumed_from_buf(struct rw_buffer *buf, size_t length) +{ + uint8_t *new = memdup(buf->data+length, buf->length-length); + free(buf->data); + buf->data = new; + buf->length -= length; +} + +static BOOL write_out_of_buf(int fd, struct rw_buffer *buf) +{ + uint8_t *tmp; + size_t tmp_length, written; + + peek_into_buf(buf, &tmp, &tmp_length); + if (tmp_length == 0) + return True; + + written = write(fd, tmp, tmp_length); + if (written < 0) + return False; + + consumed_from_buf(buf, written); + return True; +} + +static BOOL ldap_append_to_buf(struct ldap_message *msg, struct rw_buffer *buf) +{ + DATA_BLOB blob; + BOOL res; + + if (!ldap_encode(msg, &blob)) + return False; + + res = append_to_buf(buf, blob.data, blob.length); + + data_blob_free(&blob); + return res; +} + +static void new_ldap_client(int listen_sock) +{ + struct sockaddr_un sunaddr; + struct winbind_ldap_client *client; + socklen_t len; + int sock; + + /* Accept connection */ + + len = sizeof(sunaddr); + + do { + sock = accept(listen_sock, (struct sockaddr *)&sunaddr, &len); + } while (sock == -1 && errno == EINTR); + + if (sock == -1) + return; + + DEBUG(6,("accepted socket %d\n", sock)); + + /* Create new connection structure */ + + client = SMB_MALLOC_P(struct winbind_ldap_client); + + if (client == NULL) + return; + + ZERO_STRUCTP(client); + + client->sock = sock; + client->finished = False; + + DLIST_ADD(ldap_clients, client); +} + +static struct ldap_message *get_msg_from_buf(struct rw_buffer *buffer, + BOOL *error) +{ + uint8_t *buf; + int buf_length, msg_length; + DATA_BLOB blob; + ASN1_DATA data; + struct ldap_message *msg; + + DEBUG(10,("ldapsrv_recv\n")); + + *error = False; + + peek_into_buf(buffer, &buf, &buf_length); + + if (buf_length < 8) { + /* Arbitrary heuristics: ldap messages are longer than eight + * bytes, and their tag length fits into the eight bytes */ + return NULL; + } + + /* LDAP Messages are always SEQUENCES */ + + if (!asn1_object_length(buf, buf_length, ASN1_SEQUENCE(0), + &msg_length)) + goto disconnect; + + if (buf_length < msg_length) { + /* Not enough yet */ + return NULL; + } + + /* We've got a complete LDAP request in the in-buffer */ + + blob.data = buf; + blob.length = msg_length; + + if (!asn1_load(&data, blob)) + goto disconnect; + + msg = new_ldap_message(); + + if ((msg == NULL) || !ldap_decode(&data, msg)) { + asn1_free(&data); + goto disconnect; + } + + asn1_free(&data); + + consumed_from_buf(buffer, msg_length); + + return msg; + + disconnect: + + *error = True; + return NULL; +} + +static int send_msg_to_server(struct ldap_message *msg, + struct winbind_ldap_server *server) +{ + int cli_messageid; + + cli_messageid = msg->messageid; + msg->messageid = ldap_servers->messageid; + + if (!ldap_append_to_buf(msg, &ldap_servers->out_buffer)) + return -1; + + msg->messageid = cli_messageid; + return ldap_servers->messageid++; +} + +static int send_msg(struct ldap_message *msg) +{ + /* This is the scheduling routine that should decide where to send + * stuff. The first attempt is easy: We only have one server. This + * will change once we handle referrals etc. */ + + SMB_ASSERT(ldap_servers != NULL); + + if (!ldap_servers->ready) + return -1; + + return send_msg_to_server(msg, ldap_servers); +} + +static void fake_bind_response(struct winbind_ldap_client *client, + int messageid) +{ + struct ldap_message *msg = new_ldap_message(); + + if (msg == NULL) { + client->finished = True; + return; + } + + msg->messageid = messageid; + msg->type = LDAP_TAG_BindResponse; + msg->r.BindResponse.response.resultcode = 0; + msg->r.BindResponse.response.dn = ""; + msg->r.BindResponse.response.dn = ""; + msg->r.BindResponse.response.errormessage = ""; + msg->r.BindResponse.response.referral = ""; + ldap_append_to_buf(msg, &client->out_buffer); + destroy_ldap_message(msg); +} + +static int open_ldap_socket(void) +{ + static int fd = -1; + + if (fd >= 0) + return fd; + + fd = create_pipe_sock(get_winbind_priv_pipe_dir(), "ldapi", 0750); + return fd; +} + +static BOOL do_sigterm = False; + +static void ldap_termination_handler(int signum) +{ + do_sigterm = True; + sys_select_signal(); +} + +static BOOL handled_locally(struct ldap_message *msg, + struct winbind_ldap_server *server) +{ + struct ldap_Result *r = &msg->r.BindResponse.response; + + if (msg->type != LDAP_TAG_BindResponse) + return False; + + if (r->resultcode != 0) { + destroy_ldap_message(msg); + server->finished = True; + } + destroy_ldap_message(msg); + server->ready = True; + return True; +} + +static void client_has_data(struct winbind_ldap_client *client) +{ + + struct ldap_message *msg; + + if (!read_into_buf(client->sock, &client->in_buffer)) { + client->finished = True; + return; + } + + while ((msg = get_msg_from_buf(&client->in_buffer, + &client->finished))) { + struct pending_ldap_message *pending; + + if (msg->type == LDAP_TAG_BindRequest) { + fake_bind_response(client, msg->messageid); + destroy_ldap_message(msg); + continue; + } + + if (msg->type == LDAP_TAG_UnbindRequest) { + destroy_ldap_message(msg); + client->finished = True; + break; + } + + pending = SMB_MALLOC_P(struct pending_ldap_message); + if (pending == NULL) + continue; + + pending->msg = msg; + pending->client = client; + pending->our_msgid = send_msg(msg); + + if (pending->our_msgid < 0) { + /* could not send */ + client->finished = True; + free(pending); + } + DLIST_ADD(pending_messages, pending); + } +} + +static struct ldap_Result *ldap_msg2result(struct ldap_message *msg) +{ + switch(msg->type) { + case LDAP_TAG_BindResponse: + return &msg->r.BindResponse.response; + case LDAP_TAG_SearchResultDone: + return &msg->r.SearchResultDone; + case LDAP_TAG_ModifyResponse: + return &msg->r.ModifyResponse; + case LDAP_TAG_AddResponse: + return &msg->r.AddResponse; + case LDAP_TAG_DelResponse: + return &msg->r.DelResponse; + case LDAP_TAG_ModifyDNResponse: + return &msg->r.ModifyDNResponse; + case LDAP_TAG_CompareResponse: + return &msg->r.CompareResponse; + case LDAP_TAG_ExtendedResponse: + return &msg->r.ExtendedResponse.response; + } + return NULL; +} + +static void server_has_data(struct winbind_ldap_server *server) +{ + struct ldap_message *msg; + + if (!read_into_buf(server->sock, &server->in_buffer)) { + server->finished = True; + return; + } + + while ((msg = get_msg_from_buf(&server->in_buffer, + &server->finished))) { + struct pending_ldap_message *pending; + struct rw_buffer *buf; + struct ldap_Result *res; + + if (handled_locally(msg, server)) + continue; + + res = ldap_msg2result(msg); + + if ( (res != NULL) && (res->resultcode == 10) ) + DEBUG(5, ("Got Referral %s\n", res->referral)); + + for (pending = pending_messages; + pending != NULL; + pending = pending->next) { + if (pending->our_msgid == msg->messageid) + break; + } + + if (pending == NULL) { + talloc_destroy(msg->mem_ctx); + continue; + } + + msg->messageid = pending->msg->messageid; + + buf = &pending->client->out_buffer; + ldap_append_to_buf(msg, buf); + + if ( (msg->type != LDAP_TAG_SearchResultEntry) && + (msg->type != LDAP_TAG_SearchResultReference) ) { + destroy_ldap_message(pending->msg); + DLIST_REMOVE(pending_messages, + pending); + SAFE_FREE(pending); + } + destroy_ldap_message(msg); + } +} + +static void process_ldap_loop(void) +{ + struct winbind_ldap_client *client; + struct winbind_ldap_server *server; + fd_set r_fds, w_fds; + int maxfd, listen_sock, selret; + struct timeval timeout; + + /* Free up temporary memory */ + + lp_talloc_free(); + main_loop_talloc_free(); + + if (do_sigterm) { +#if 0 + TALLOC_CTX *mem_ctx = talloc_init("describe"); + DEBUG(0, ("%s\n", talloc_describe_all(mem_ctx))); + talloc_destroy(mem_ctx); +#endif + exit(0); + } + + /* Initialise fd lists for select() */ + + listen_sock = open_ldap_socket(); + + if (listen_sock == -1) { + perror("open_ldap_socket"); + exit(1); + } + + maxfd = listen_sock; + + FD_ZERO(&r_fds); + FD_ZERO(&w_fds); + FD_SET(listen_sock, &r_fds); + + timeout.tv_sec = WINBINDD_ESTABLISH_LOOP; + timeout.tv_usec = 0; + + /* Set up client readers and writers */ + + client = ldap_clients; + + while (client != NULL) { + + if (client->finished) { + struct winbind_ldap_client *next = client->next; + DLIST_REMOVE(ldap_clients, client); + close(client->sock); + SAFE_FREE(client->in_buffer.data); + SAFE_FREE(client->out_buffer.data); + SAFE_FREE(client); + client = next; + continue; + } + + if (client->sock > maxfd) + maxfd = client->sock; + + FD_SET(client->sock, &r_fds); + + if (client->out_buffer.length > 0) + FD_SET(client->sock, &w_fds); + + client = client->next; + } + + /* And now the servers */ + + server = ldap_servers; + + while (server != NULL) { + + if (server->finished) { + struct winbind_ldap_server *next = server->next; + DLIST_REMOVE(ldap_servers, server); + close(server->sock); + SAFE_FREE(server); + server = next; + continue; + } + + if (server->sock > maxfd) + maxfd = server->sock; + + FD_SET(server->sock, &r_fds); + + if (server->out_buffer.length > 0) + FD_SET(server->sock, &w_fds); + + server = server->next; + } + + selret = sys_select(maxfd + 1, &r_fds, &w_fds, NULL, &timeout); + + if (selret == 0) + return; + + if (selret == -1 && errno != EINTR) { + perror("select"); + exit(1); + } + + if (FD_ISSET(listen_sock, &r_fds)) + new_ldap_client(listen_sock); + + for (client = ldap_clients; client != NULL; client = client->next) { + + if (FD_ISSET(client->sock, &r_fds)) + client_has_data(client); + + if ((!client->finished) && FD_ISSET(client->sock, &w_fds)) + write_out_of_buf(client->sock, &client->out_buffer); + } + + for (server = ldap_servers; server != NULL; server = server->next) { + + if (FD_ISSET(server->sock, &r_fds)) + server_has_data(server); + + if (!server->finished && FD_ISSET(server->sock, &w_fds)) + write_out_of_buf(server->sock, &server->out_buffer); + } +} + +static BOOL setup_ldap_serverconn(void) +{ + char *host; + uint16 port; + BOOL ldaps; + struct hostent *hp; + struct in_addr ip; + TALLOC_CTX *mem_ctx = talloc_init("server"); + struct ldap_message *msg; + char *dn, *pw; + + ldap_servers = SMB_MALLOC_P(struct winbind_ldap_server); + + if ((ldap_servers == NULL) || (mem_ctx == NULL)) + return False; + + if (!ldap_parse_basic_url(mem_ctx, "ldap://192.168.234.1:3899/", + &host, &port, &ldaps)) + return False; + + hp = sys_gethostbyname(host); + + if ((hp == NULL) || (hp->h_addr == NULL)) + return False; + + putip((char *)&ip, (char *)hp->h_addr); + + ZERO_STRUCTP(ldap_servers); + ldap_servers->sock = open_socket_out(SOCK_STREAM, &ip, port, 10000); + ldap_servers->messageid = 1; + + if (!fetch_ldap_pw(&dn, &pw)) + return False; + + msg = new_ldap_simple_bind_msg(dn, pw); + + SAFE_FREE(dn); + SAFE_FREE(pw); + + if (msg == NULL) + return False; + + msg->messageid = ldap_servers->messageid++; + + ldap_append_to_buf(msg, &ldap_servers->out_buffer); + + destroy_ldap_message(msg); + + return (ldap_servers->sock >= 0); +} + +void do_ldap_proxy(void) +{ + int ldap_child; + + ldap_child = sys_fork(); + + if (ldap_child != 0) + return; + + /* tdb needs special fork handling */ + if (tdb_reopen_all() == -1) { + DEBUG(0,("tdb_reopen_all failed.\n")); + _exit(0); + } + + if (!message_init()) { + DEBUG(0, ("message_init failed\n")); + _exit(0); + } + + CatchSignal(SIGINT, ldap_termination_handler); + CatchSignal(SIGQUIT, ldap_termination_handler); + CatchSignal(SIGTERM, ldap_termination_handler); + + if (!setup_ldap_serverconn()) + return; + + while (1) + process_ldap_loop(); + + return; +} |