1/* 2 Unix SMB/CIFS implementation. 3 4 Connect GENSEC to an external SASL lib 5 6 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 3 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program. If not, see <http://www.gnu.org/licenses/>. 20*/ 21 22#include "includes.h" 23#include "auth/auth.h" 24#include "auth/credentials/credentials.h" 25#include "auth/gensec/gensec.h" 26#include "auth/gensec/gensec_proto.h" 27#include "lib/socket/socket.h" 28#include <sasl/sasl.h> 29 30struct gensec_sasl_state { 31 sasl_conn_t *conn; 32 int step; 33}; 34 35static NTSTATUS sasl_nt_status(int sasl_ret) 36{ 37 switch (sasl_ret) { 38 case SASL_CONTINUE: 39 return NT_STATUS_MORE_PROCESSING_REQUIRED; 40 case SASL_NOMEM: 41 return NT_STATUS_NO_MEMORY; 42 case SASL_BADPARAM: 43 case SASL_NOMECH: 44 return NT_STATUS_INVALID_PARAMETER; 45 case SASL_BADMAC: 46 return NT_STATUS_ACCESS_DENIED; 47 case SASL_OK: 48 return NT_STATUS_OK; 49 default: 50 return NT_STATUS_UNSUCCESSFUL; 51 } 52} 53 54static int gensec_sasl_get_user(void *context, int id, 55 const char **result, unsigned *len) 56{ 57 struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security); 58 const char *username = cli_credentials_get_username(gensec_get_credentials(gensec_security)); 59 if (id != SASL_CB_USER && id != SASL_CB_AUTHNAME) { 60 return SASL_FAIL; 61 } 62 63 *result = username; 64 return SASL_OK; 65} 66 67static int gensec_sasl_get_realm(void *context, int id, 68 const char **availrealms, 69 const char **result) 70{ 71 struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security); 72 const char *realm = cli_credentials_get_realm(gensec_get_credentials(gensec_security)); 73 int i; 74 if (id != SASL_CB_GETREALM) { 75 return SASL_FAIL; 76 } 77 78 for (i=0; availrealms && availrealms[i]; i++) { 79 if (strcasecmp_m(realm, availrealms[i]) == 0) { 80 result[i] = availrealms[i]; 81 return SASL_OK; 82 } 83 } 84 /* None of the realms match, so lets not specify one */ 85 *result = ""; 86 return SASL_OK; 87} 88 89static int gensec_sasl_get_password(sasl_conn_t *conn, void *context, int id, 90 sasl_secret_t **psecret) 91{ 92 struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security); 93 const char *password = cli_credentials_get_password(gensec_get_credentials(gensec_security)); 94 95 sasl_secret_t *secret; 96 if (!password) { 97 *psecret = NULL; 98 return SASL_OK; 99 } 100 secret = talloc_size(gensec_security, sizeof(sasl_secret_t)+strlen(password)); 101 if (!secret) { 102 return SASL_NOMEM; 103 } 104 secret->len = strlen(password); 105 safe_strcpy((char*)secret->data, password, secret->len+1); 106 *psecret = secret; 107 return SASL_OK; 108} 109 110static int gensec_sasl_dispose(struct gensec_sasl_state *gensec_sasl_state) 111{ 112 sasl_dispose(&gensec_sasl_state->conn); 113 return SASL_OK; 114} 115 116static NTSTATUS gensec_sasl_client_start(struct gensec_security *gensec_security) 117{ 118 struct gensec_sasl_state *gensec_sasl_state; 119 const char *service = gensec_get_target_service(gensec_security); 120 const char *target_name = gensec_get_target_hostname(gensec_security); 121 struct socket_address *local_socket_addr = gensec_get_my_addr(gensec_security); 122 struct socket_address *remote_socket_addr = gensec_get_peer_addr(gensec_security); 123 char *local_addr = NULL; 124 char *remote_addr = NULL; 125 int sasl_ret; 126 127 sasl_callback_t *callbacks; 128 129 gensec_sasl_state = talloc(gensec_security, struct gensec_sasl_state); 130 if (!gensec_sasl_state) { 131 return NT_STATUS_NO_MEMORY; 132 } 133 134 callbacks = talloc_array(gensec_sasl_state, sasl_callback_t, 5); 135 callbacks[0].id = SASL_CB_USER; 136 callbacks[0].proc = gensec_sasl_get_user; 137 callbacks[0].context = gensec_security; 138 139 callbacks[1].id = SASL_CB_AUTHNAME; 140 callbacks[1].proc = gensec_sasl_get_user; 141 callbacks[1].context = gensec_security; 142 143 callbacks[2].id = SASL_CB_GETREALM; 144 callbacks[2].proc = gensec_sasl_get_realm; 145 callbacks[2].context = gensec_security; 146 147 callbacks[3].id = SASL_CB_PASS; 148 callbacks[3].proc = gensec_sasl_get_password; 149 callbacks[3].context = gensec_security; 150 151 callbacks[4].id = SASL_CB_LIST_END; 152 callbacks[4].proc = NULL; 153 callbacks[4].context = NULL; 154 155 gensec_security->private_data = gensec_sasl_state; 156 157 if (local_socket_addr) { 158 local_addr = talloc_asprintf(gensec_sasl_state, 159 "%s;%d", 160 local_socket_addr->addr, 161 local_socket_addr->port); 162 } 163 164 if (remote_socket_addr) { 165 remote_addr = talloc_asprintf(gensec_sasl_state, 166 "%s;%d", 167 remote_socket_addr->addr, 168 remote_socket_addr->port); 169 } 170 gensec_sasl_state->step = 0; 171 172 sasl_ret = sasl_client_new(service, 173 target_name, 174 local_addr, remote_addr, callbacks, 0, 175 &gensec_sasl_state->conn); 176 177 if (sasl_ret == SASL_OK || sasl_ret == SASL_CONTINUE) { 178 sasl_security_properties_t props; 179 talloc_set_destructor(gensec_sasl_state, gensec_sasl_dispose); 180 181 ZERO_STRUCT(props); 182 if (gensec_security->want_features & GENSEC_FEATURE_SIGN) { 183 props.min_ssf = 1; 184 } 185 if (gensec_security->want_features & GENSEC_FEATURE_SEAL) { 186 props.min_ssf = 40; 187 } 188 189 props.max_ssf = UINT_MAX; 190 props.maxbufsize = 65536; 191 sasl_ret = sasl_setprop(gensec_sasl_state->conn, SASL_SEC_PROPS, &props); 192 if (sasl_ret != SASL_OK) { 193 return sasl_nt_status(sasl_ret); 194 } 195 196 } else { 197 DEBUG(1, ("GENSEC SASL: client_new failed: %s\n", sasl_errdetail(gensec_sasl_state->conn))); 198 } 199 return sasl_nt_status(sasl_ret); 200} 201 202static NTSTATUS gensec_sasl_update(struct gensec_security *gensec_security, 203 TALLOC_CTX *out_mem_ctx, 204 const DATA_BLOB in, DATA_BLOB *out) 205{ 206 struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data, 207 struct gensec_sasl_state); 208 int sasl_ret; 209 const char *out_data; 210 unsigned int out_len; 211 212 if (gensec_sasl_state->step == 0) { 213 const char *mech; 214 sasl_ret = sasl_client_start(gensec_sasl_state->conn, gensec_security->ops->sasl_name, 215 NULL, &out_data, &out_len, &mech); 216 } else { 217 sasl_ret = sasl_client_step(gensec_sasl_state->conn, 218 (char*)in.data, in.length, NULL, 219 &out_data, &out_len); 220 } 221 if (sasl_ret == SASL_OK || sasl_ret == SASL_CONTINUE) { 222 *out = data_blob_talloc(out_mem_ctx, out_data, out_len); 223 } else { 224 DEBUG(1, ("GENSEC SASL: step %d update failed: %s\n", gensec_sasl_state->step, 225 sasl_errdetail(gensec_sasl_state->conn))); 226 } 227 gensec_sasl_state->step++; 228 return sasl_nt_status(sasl_ret); 229} 230 231static NTSTATUS gensec_sasl_unwrap_packets(struct gensec_security *gensec_security, 232 TALLOC_CTX *out_mem_ctx, 233 const DATA_BLOB *in, 234 DATA_BLOB *out, 235 size_t *len_processed) 236{ 237 struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data, 238 struct gensec_sasl_state); 239 const char *out_data; 240 unsigned int out_len; 241 242 int sasl_ret = sasl_decode(gensec_sasl_state->conn, 243 (char*)in->data, in->length, &out_data, 244 &out_len); 245 if (sasl_ret == SASL_OK) { 246 *out = data_blob_talloc(out_mem_ctx, out_data, out_len); 247 *len_processed = in->length; 248 } else { 249 DEBUG(1, ("GENSEC SASL: unwrap failed: %s\n", sasl_errdetail(gensec_sasl_state->conn))); 250 } 251 return sasl_nt_status(sasl_ret); 252 253} 254 255static NTSTATUS gensec_sasl_wrap_packets(struct gensec_security *gensec_security, 256 TALLOC_CTX *out_mem_ctx, 257 const DATA_BLOB *in, 258 DATA_BLOB *out, 259 size_t *len_processed) 260{ 261 struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data, 262 struct gensec_sasl_state); 263 const char *out_data; 264 unsigned int out_len; 265 266 int sasl_ret = sasl_encode(gensec_sasl_state->conn, 267 (char*)in->data, in->length, &out_data, 268 &out_len); 269 if (sasl_ret == SASL_OK) { 270 *out = data_blob_talloc(out_mem_ctx, out_data, out_len); 271 *len_processed = in->length; 272 } else { 273 DEBUG(1, ("GENSEC SASL: wrap failed: %s\n", sasl_errdetail(gensec_sasl_state->conn))); 274 } 275 return sasl_nt_status(sasl_ret); 276} 277 278/* Try to figure out what features we actually got on the connection */ 279static bool gensec_sasl_have_feature(struct gensec_security *gensec_security, 280 uint32_t feature) 281{ 282 struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data, 283 struct gensec_sasl_state); 284 sasl_ssf_t ssf; 285 int sasl_ret = sasl_getprop(gensec_sasl_state->conn, SASL_SSF, 286 (const void**)&ssf); 287 if (sasl_ret != SASL_OK) { 288 return false; 289 } 290 if (feature & GENSEC_FEATURE_SIGN) { 291 if (ssf == 0) { 292 return false; 293 } 294 if (ssf >= 1) { 295 return true; 296 } 297 } 298 if (feature & GENSEC_FEATURE_SEAL) { 299 if (ssf <= 1) { 300 return false; 301 } 302 if (ssf > 1) { 303 return true; 304 } 305 } 306 return false; 307} 308 309/* This could in theory work with any SASL mech */ 310static const struct gensec_security_ops gensec_sasl_security_ops = { 311 .name = "sasl-DIGEST-MD5", 312 .sasl_name = "DIGEST-MD5", 313 .client_start = gensec_sasl_client_start, 314 .update = gensec_sasl_update, 315 .wrap_packets = gensec_sasl_wrap_packets, 316 .unwrap_packets = gensec_sasl_unwrap_packets, 317 .have_feature = gensec_sasl_have_feature, 318 .enabled = true, 319 .priority = GENSEC_SASL 320}; 321 322static int gensec_sasl_log(void *context, 323 int sasl_log_level, 324 const char *message) 325{ 326 int dl; 327 switch (sasl_log_level) { 328 case SASL_LOG_NONE: 329 dl = 0; 330 break; 331 case SASL_LOG_ERR: 332 dl = 1; 333 break; 334 case SASL_LOG_FAIL: 335 dl = 2; 336 break; 337 case SASL_LOG_WARN: 338 dl = 3; 339 break; 340 case SASL_LOG_NOTE: 341 dl = 5; 342 break; 343 case SASL_LOG_DEBUG: 344 dl = 10; 345 break; 346 case SASL_LOG_TRACE: 347 dl = 11; 348 break; 349#if DEBUG_PASSWORD 350 case SASL_LOG_PASS: 351 dl = 100; 352 break; 353#endif 354 default: 355 dl = 0; 356 break; 357 } 358 DEBUG(dl, ("gensec_sasl: %s\n", message)); 359 360 return SASL_OK; 361} 362 363NTSTATUS gensec_sasl_init(void) 364{ 365 NTSTATUS ret; 366 int sasl_ret; 367#if 0 368 int i; 369 const char **sasl_mechs; 370#endif 371 372 static const sasl_callback_t callbacks[] = { 373 { 374 .id = SASL_CB_LOG, 375 .proc = gensec_sasl_log, 376 .context = NULL, 377 }, 378 { 379 .id = SASL_CB_LIST_END, 380 .proc = gensec_sasl_log, 381 .context = NULL, 382 } 383 }; 384 sasl_ret = sasl_client_init(callbacks); 385 386 if (sasl_ret == SASL_NOMECH) { 387 /* Nothing to do here */ 388 return NT_STATUS_OK; 389 } 390 391 if (sasl_ret != SASL_OK) { 392 return sasl_nt_status(sasl_ret); 393 } 394 395 /* For now, we just register DIGEST-MD5 */ 396#if 1 397 ret = gensec_register(&gensec_sasl_security_ops); 398 if (!NT_STATUS_IS_OK(ret)) { 399 DEBUG(0,("Failed to register '%s' gensec backend!\n", 400 gensec_sasl_security_ops.name)); 401 return ret; 402 } 403#else 404 sasl_mechs = sasl_global_listmech(); 405 for (i = 0; sasl_mechs && sasl_mechs[i]; i++) { 406 const struct gensec_security_ops *oldmech; 407 struct gensec_security_ops *newmech; 408 oldmech = gensec_security_by_sasl_name(NULL, sasl_mechs[i]); 409 if (oldmech) { 410 continue; 411 } 412 newmech = talloc(talloc_autofree_context(), struct gensec_security_ops); 413 if (!newmech) { 414 return NT_STATUS_NO_MEMORY; 415 } 416 *newmech = gensec_sasl_security_ops; 417 newmech->sasl_name = talloc_strdup(newmech, sasl_mechs[i]); 418 newmech->name = talloc_asprintf(newmech, "sasl-%s", sasl_mechs[i]); 419 if (!newmech->sasl_name || !newmech->name) { 420 return NT_STATUS_NO_MEMORY; 421 } 422 423 ret = gensec_register(newmech); 424 if (!NT_STATUS_IS_OK(ret)) { 425 DEBUG(0,("Failed to register '%s' gensec backend!\n", 426 gensec_sasl_security_ops.name)); 427 return ret; 428 } 429 } 430#endif 431 return NT_STATUS_OK; 432} 433