s3:tldap: add support for [START]TLS
[samba.git] / source3 / lib / tldap_tls_connect.c
1 /*
2  * Unix SMB/CIFS implementation.
3  * tls based tldap connect
4  * Copyright (C) Stefan Metzmacher 2024
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "replace.h"
21 #include "tldap.h"
22 #include "tldap_tls_connect.h"
23 #include "lib/util/samba_util.h"
24 #include "lib/util/debug.h"
25 #include "lib/param/param.h"
26 #include "../libcli/util/ntstatus.h"
27 #include "../source4/lib/tls/tls.h"
28
29 struct tldap_tls_connect_state {
30         struct tevent_context *ev;
31         struct tldap_context *ctx;
32         struct loadparm_context *lp_ctx;
33         const char *peer_name;
34 };
35
36 static void tldap_tls_connect_starttls_done(struct tevent_req *subreq);
37 static void tldap_tls_connect_crypto_start(struct tevent_req *req);
38 static void tldap_tls_connect_crypto_done(struct tevent_req *subreq);
39
40 struct tevent_req *tldap_tls_connect_send(
41         TALLOC_CTX *mem_ctx,
42         struct tevent_context *ev,
43         struct tldap_context *ctx,
44         struct loadparm_context *lp_ctx,
45         const char *peer_name)
46 {
47         struct tevent_req *req = NULL;
48         struct tldap_tls_connect_state *state = NULL;
49
50         req = tevent_req_create(mem_ctx, &state,
51                                 struct tldap_tls_connect_state);
52         if (req == NULL) {
53                 return NULL;
54         }
55         state->ev = ev;
56         state->ctx = ctx;
57         state->lp_ctx = lp_ctx;
58         state->peer_name = peer_name;
59
60         if (!tldap_connection_ok(ctx)) {
61                 DBG_ERR("tldap_connection_ok() => false\n");
62                 tevent_req_ldap_error(req, TLDAP_CONNECT_ERROR);
63                 return tevent_req_post(req, ev);
64         }
65
66         if (tldap_has_gensec_tstream(ctx)) {
67                 DBG_ERR("tldap_has_gensec_tstream() => true\n");
68                 tevent_req_ldap_error(req, TLDAP_LOCAL_ERROR);
69                 return tevent_req_post(req, ev);
70         }
71
72         if (tldap_get_starttls_needed(ctx)) {
73                 struct tevent_req *subreq = NULL;
74                 static const char *start_tls_oid = "1.3.6.1.4.1.1466.20037";
75
76                 subreq = tldap_extended_send(state,
77                                              state->ev,
78                                              state->ctx,
79                                              start_tls_oid,
80                                              NULL, /* in_blob */
81                                              NULL, /* sctrls */
82                                              0, /* num_sctrls */
83                                              NULL, /* cctrls */
84                                              0); /* num_cctrls */
85                 if (tevent_req_nomem(subreq, req)) {
86                         return tevent_req_post(req, ev);
87                 }
88                 tevent_req_set_callback(subreq,
89                                         tldap_tls_connect_starttls_done,
90                                         req);
91
92                 return req;
93         }
94
95         tldap_tls_connect_crypto_start(req);
96         if (!tevent_req_is_in_progress(req)) {
97                 return tevent_req_post(req, ev);
98         }
99
100         return req;
101 }
102
103 static void tldap_tls_connect_starttls_done(struct tevent_req *subreq)
104 {
105         struct tevent_req *req = tevent_req_callback_data(
106                 subreq, struct tevent_req);
107         struct tldap_tls_connect_state *state = tevent_req_data(
108                 req, struct tldap_tls_connect_state);
109         TLDAPRC rc;
110
111         rc = tldap_extended_recv(subreq, state, NULL, NULL);
112         TALLOC_FREE(subreq);
113         if (!TLDAP_RC_IS_SUCCESS(rc)) {
114                 DBG_ERR("tldap_extended_recv(STARTTLS, %s): %s\n",
115                         state->peer_name, tldap_rc2string(rc));
116                 tevent_req_ldap_error(req, rc);
117                 return;
118         }
119
120         tldap_set_starttls_needed(state->ctx, false);
121
122         tldap_tls_connect_crypto_start(req);
123 }
124
125 static void tldap_tls_connect_crypto_start(struct tevent_req *req)
126 {
127         struct tldap_tls_connect_state *state = tevent_req_data(
128                 req, struct tldap_tls_connect_state);
129         struct tstream_context *plain_stream = NULL;
130         struct tstream_tls_params *tls_params = NULL;
131         struct tevent_req *subreq = NULL;
132         NTSTATUS status;
133
134         plain_stream = tldap_get_plain_tstream(state->ctx);
135         if (plain_stream == NULL) {
136                 DBG_ERR("tldap_get_plain_tstream() = NULL\n");
137                 tevent_req_ldap_error(req, TLDAP_LOCAL_ERROR);
138                 return;
139         }
140
141         status = tstream_tls_params_client_lpcfg(state,
142                                                  state->lp_ctx,
143                                                  state->peer_name,
144                                                  &tls_params);
145         if (!NT_STATUS_IS_OK(status)) {
146                 DBG_ERR("tstream_tls_params_client_lpcfg(%s): %s\n",
147                         state->peer_name, nt_errstr(status));
148                 tevent_req_ldap_error(req, TLDAP_LOCAL_ERROR);
149                 return;
150         }
151
152         subreq = tstream_tls_connect_send(state,
153                                           state->ev,
154                                           plain_stream,
155                                           tls_params);
156         if (tevent_req_nomem(subreq, req)) {
157                 return;
158         }
159         tevent_req_set_callback(subreq,
160                                 tldap_tls_connect_crypto_done,
161                                 req);
162 }
163
164 static void tldap_tls_connect_crypto_done(struct tevent_req *subreq)
165 {
166         struct tevent_req *req = tevent_req_callback_data(
167                 subreq, struct tevent_req);
168         struct tldap_tls_connect_state *state = tevent_req_data(
169                 req, struct tldap_tls_connect_state);
170         struct tstream_context *tls_stream = NULL;
171         int ret;
172         int error;
173
174         ret = tstream_tls_connect_recv(subreq, &error, state, &tls_stream);
175         TALLOC_FREE(subreq);
176         if (ret != 0) {
177                 DBG_ERR("tstream_tls_connect_recv(%s): %d %d\n",
178                         state->peer_name, ret, error);
179                 tevent_req_ldap_error(req, TLDAP_CONNECT_ERROR);
180                 return;
181         }
182
183         tldap_set_tls_tstream(state->ctx, &tls_stream);
184
185         tevent_req_done(req);
186 }
187
188 TLDAPRC tldap_tls_connect_recv(struct tevent_req *req)
189 {
190         TLDAPRC rc;
191
192         if (tevent_req_is_ldap_error(req, &rc)) {
193                 return rc;
194         }
195
196         return TLDAP_SUCCESS;
197 }
198
199 TLDAPRC tldap_tls_connect(
200         struct tldap_context *ctx,
201         struct loadparm_context *lp_ctx,
202         const char *peer_name)
203 {
204         TALLOC_CTX *frame = talloc_stackframe();
205         struct tevent_context *ev;
206         struct tevent_req *req;
207         TLDAPRC rc = TLDAP_NO_MEMORY;
208
209         ev = samba_tevent_context_init(frame);
210         if (ev == NULL) {
211                 goto fail;
212         }
213         req = tldap_tls_connect_send(frame,
214                                      ev,
215                                      ctx,
216                                      lp_ctx,
217                                      peer_name);
218         if (req == NULL) {
219                 goto fail;
220         }
221         if (!tevent_req_poll(req, ev)) {
222                 rc = TLDAP_OPERATIONS_ERROR;
223                 goto fail;
224         }
225         rc = tldap_tls_connect_recv(req);
226  fail:
227         TALLOC_FREE(frame);
228         return rc;
229 }