2 Unix SMB/CIFS implementation.
4 Generic Authentication Interface
6 Copyright (C) Andrew Tridgell 2003
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "auth/auth.h"
27 /* the list of currently registered GENSEC backends */
28 const static struct gensec_security_ops **generic_security_ops;
29 static int gensec_num_backends;
31 static const struct gensec_security_ops *gensec_security_by_authtype(uint8_t auth_type)
34 for (i=0; i < gensec_num_backends; i++) {
35 if (generic_security_ops[i]->auth_type == auth_type) {
36 return generic_security_ops[i];
43 static const struct gensec_security_ops *gensec_security_by_oid(const char *oid_string)
46 for (i=0; i < gensec_num_backends; i++) {
47 if (generic_security_ops[i]->oid &&
48 (strcmp(generic_security_ops[i]->oid, oid_string) == 0)) {
49 return generic_security_ops[i];
56 static const struct gensec_security_ops *gensec_security_by_sasl_name(const char *sasl_name)
59 for (i=0; i < gensec_num_backends; i++) {
60 if (generic_security_ops[i]->sasl_name
61 && (strcmp(generic_security_ops[i]->sasl_name, sasl_name) == 0)) {
62 return generic_security_ops[i];
69 static const struct gensec_security_ops *gensec_security_by_name(const char *name)
72 for (i=0; i < gensec_num_backends; i++) {
73 if (generic_security_ops[i]->name
74 && (strcmp(generic_security_ops[i]->name, name) == 0)) {
75 return generic_security_ops[i];
82 const struct gensec_security_ops **gensec_security_all(int *num_backends_out)
84 *num_backends_out = gensec_num_backends;
85 return generic_security_ops;
88 const char **gensec_security_oids(TALLOC_CTX *mem_ctx, const char *skip)
91 const char **oid_list;
93 const struct gensec_security_ops **ops = gensec_security_all(&num_backends);
97 oid_list = talloc_array(mem_ctx, const char *, num_backends + 1);
102 for (i=0; i<num_backends; i++) {
107 if (skip && strcmp(skip, ops[i]->oid)==0) {
111 oid_list[j] = ops[i]->oid;
119 Start the GENSEC system, returning a context pointer.
120 @param mem_ctx The parent TALLOC memory context.
121 @param gensec_security Returned GENSEC context pointer.
122 @note The mem_ctx is only a parent and may be NULL.
124 static NTSTATUS gensec_start(TALLOC_CTX *mem_ctx, struct gensec_security **gensec_security)
126 (*gensec_security) = talloc(mem_ctx, struct gensec_security);
127 if (!(*gensec_security)) {
128 return NT_STATUS_NO_MEMORY;
131 (*gensec_security)->ops = NULL;
133 ZERO_STRUCT((*gensec_security)->target);
135 (*gensec_security)->subcontext = False;
136 (*gensec_security)->want_features = 0;
141 * Start a GENSEC subcontext, with a copy of the properties of the parent
142 * @param mem_ctx The parent TALLOC memory context.
143 * @param parent The parent GENSEC context
144 * @param gensec_security Returned GENSEC context pointer.
145 * @note Used by SPNEGO in particular, for the actual implementation mechanism
148 NTSTATUS gensec_subcontext_start(TALLOC_CTX *mem_ctx,
149 struct gensec_security *parent,
150 struct gensec_security **gensec_security)
152 (*gensec_security) = talloc(mem_ctx, struct gensec_security);
153 if (!(*gensec_security)) {
154 return NT_STATUS_NO_MEMORY;
157 (**gensec_security) = *parent;
158 (*gensec_security)->ops = NULL;
159 (*gensec_security)->private_data = NULL;
161 (*gensec_security)->subcontext = True;
167 Start the GENSEC system, in client mode, returning a context pointer.
168 @param mem_ctx The parent TALLOC memory context.
169 @param gensec_security Returned GENSEC context pointer.
170 @note The mem_ctx is only a parent and may be NULL.
172 NTSTATUS gensec_client_start(TALLOC_CTX *mem_ctx, struct gensec_security **gensec_security)
175 status = gensec_start(mem_ctx, gensec_security);
176 if (!NT_STATUS_IS_OK(status)) {
179 (*gensec_security)->gensec_role = GENSEC_CLIENT;
180 (*gensec_security)->password_callback = NULL;
186 Start the GENSEC system, in server mode, returning a context pointer.
187 @param mem_ctx The parent TALLOC memory context.
188 @param gensec_security Returned GENSEC context pointer.
189 @note The mem_ctx is only a parent and may be NULL.
191 NTSTATUS gensec_server_start(TALLOC_CTX *mem_ctx, struct gensec_security **gensec_security)
194 status = gensec_start(mem_ctx, gensec_security);
195 if (!NT_STATUS_IS_OK(status)) {
198 (*gensec_security)->gensec_role = GENSEC_SERVER;
203 static NTSTATUS gensec_start_mech(struct gensec_security *gensec_security)
206 DEBUG(5, ("Starting GENSEC %smechanism %s\n",
207 gensec_security->subcontext ? "sub" : "",
208 gensec_security->ops->name));
209 switch (gensec_security->gensec_role) {
211 if (gensec_security->ops->client_start) {
212 status = gensec_security->ops->client_start(gensec_security);
213 if (!NT_STATUS_IS_OK(status)) {
214 DEBUG(1, ("Failed to start GENSEC client mech %s: %s\n",
215 gensec_security->ops->name, nt_errstr(status)));
220 if (gensec_security->ops->server_start) {
221 status = gensec_security->ops->server_start(gensec_security);
222 if (!NT_STATUS_IS_OK(status)) {
223 DEBUG(1, ("Failed to start GENSEC server mech %s: %s\n",
224 gensec_security->ops->name, nt_errstr(status)));
229 return NT_STATUS_INVALID_PARAMETER;
233 * Start a GENSEC sub-mechanism by DCERPC allocated 'auth type' number
234 * @param gensec_security GENSEC context pointer.
235 * @param auth_type DCERPC auth type
236 * @param auth_level DCERPC auth level
239 NTSTATUS gensec_start_mech_by_authtype(struct gensec_security *gensec_security,
240 uint8_t auth_type, uint8_t auth_level)
242 gensec_security->ops = gensec_security_by_authtype(auth_type);
243 if (!gensec_security->ops) {
244 DEBUG(3, ("Could not find GENSEC backend for auth_type=%d\n", (int)auth_type));
245 return NT_STATUS_INVALID_PARAMETER;
247 gensec_want_feature(gensec_security, GENSEC_FEATURE_DCE_STYLE);
248 if (auth_level == DCERPC_AUTH_LEVEL_INTEGRITY) {
249 gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN);
250 } else if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
251 gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN);
252 gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL);
253 } else if (auth_level == DCERPC_AUTH_LEVEL_CONNECT) {
254 /* Default features */
256 DEBUG(2,("auth_level %d not supported in DCE/RPC authentication\n",
258 return NT_STATUS_INVALID_PARAMETER;
261 return gensec_start_mech(gensec_security);
264 const char *gensec_get_name_by_authtype(uint8_t authtype)
266 const struct gensec_security_ops *ops;
267 ops = gensec_security_by_authtype(authtype);
275 const char *gensec_get_name_by_oid(const char *oid_string)
277 const struct gensec_security_ops *ops;
278 ops = gensec_security_by_oid(oid_string);
287 * Start a GENSEC sub-mechanism by OID, used in SPNEGO
289 * @note This should also be used when you wish to just start NLTMSSP (for example), as it uses a
290 * well-known #define to hook it in.
293 NTSTATUS gensec_start_mech_by_oid(struct gensec_security *gensec_security,
294 const char *mech_oid)
296 gensec_security->ops = gensec_security_by_oid(mech_oid);
297 if (!gensec_security->ops) {
298 DEBUG(3, ("Could not find GENSEC backend for oid=%s\n", mech_oid));
299 return NT_STATUS_INVALID_PARAMETER;
301 return gensec_start_mech(gensec_security);
305 * Start a GENSEC sub-mechanism by a well know SASL name
309 NTSTATUS gensec_start_mech_by_sasl_name(struct gensec_security *gensec_security,
310 const char *sasl_name)
312 gensec_security->ops = gensec_security_by_sasl_name(sasl_name);
313 if (!gensec_security->ops) {
314 DEBUG(3, ("Could not find GENSEC backend for sasl_name=%s\n", sasl_name));
315 return NT_STATUS_INVALID_PARAMETER;
317 return gensec_start_mech(gensec_security);
321 wrappers for the gensec function pointers
323 NTSTATUS gensec_unseal_packet(struct gensec_security *gensec_security,
325 uint8_t *data, size_t length,
326 const uint8_t *whole_pdu, size_t pdu_length,
327 const DATA_BLOB *sig)
329 if (!gensec_security->ops->unseal_packet) {
330 return NT_STATUS_NOT_IMPLEMENTED;
332 if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
333 if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
334 return gensec_check_packet(gensec_security, mem_ctx,
336 whole_pdu, pdu_length,
339 return NT_STATUS_INVALID_PARAMETER;
342 return gensec_security->ops->unseal_packet(gensec_security, mem_ctx,
344 whole_pdu, pdu_length,
348 NTSTATUS gensec_check_packet(struct gensec_security *gensec_security,
350 const uint8_t *data, size_t length,
351 const uint8_t *whole_pdu, size_t pdu_length,
352 const DATA_BLOB *sig)
354 if (!gensec_security->ops->check_packet) {
355 return NT_STATUS_NOT_IMPLEMENTED;
357 if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
358 return NT_STATUS_INVALID_PARAMETER;
361 return gensec_security->ops->check_packet(gensec_security, mem_ctx, data, length, whole_pdu, pdu_length, sig);
364 NTSTATUS gensec_seal_packet(struct gensec_security *gensec_security,
366 uint8_t *data, size_t length,
367 const uint8_t *whole_pdu, size_t pdu_length,
370 if (!gensec_security->ops->seal_packet) {
371 return NT_STATUS_NOT_IMPLEMENTED;
373 if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
374 if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
375 return gensec_sign_packet(gensec_security, mem_ctx,
377 whole_pdu, pdu_length,
380 return NT_STATUS_INVALID_PARAMETER;
383 return gensec_security->ops->seal_packet(gensec_security, mem_ctx, data, length, whole_pdu, pdu_length, sig);
386 NTSTATUS gensec_sign_packet(struct gensec_security *gensec_security,
388 const uint8_t *data, size_t length,
389 const uint8_t *whole_pdu, size_t pdu_length,
392 if (!gensec_security->ops->sign_packet) {
393 return NT_STATUS_NOT_IMPLEMENTED;
395 if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
396 return NT_STATUS_INVALID_PARAMETER;
399 return gensec_security->ops->sign_packet(gensec_security, mem_ctx, data, length, whole_pdu, pdu_length, sig);
402 size_t gensec_sig_size(struct gensec_security *gensec_security)
404 if (!gensec_security->ops->sig_size) {
407 if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
411 return gensec_security->ops->sig_size(gensec_security);
414 NTSTATUS gensec_wrap(struct gensec_security *gensec_security,
419 if (!gensec_security->ops->wrap) {
420 return NT_STATUS_NOT_IMPLEMENTED;
422 return gensec_security->ops->wrap(gensec_security, mem_ctx, in, out);
425 NTSTATUS gensec_unwrap(struct gensec_security *gensec_security,
430 if (!gensec_security->ops->unwrap) {
431 return NT_STATUS_NOT_IMPLEMENTED;
433 return gensec_security->ops->unwrap(gensec_security, mem_ctx, in, out);
436 NTSTATUS gensec_session_key(struct gensec_security *gensec_security,
437 DATA_BLOB *session_key)
439 if (!gensec_security->ops->session_key) {
440 return NT_STATUS_NOT_IMPLEMENTED;
442 return gensec_security->ops->session_key(gensec_security, session_key);
446 * Return the credentials of a logged on user, including session keys
449 * Only valid after a successful authentication
451 * May only be called once per authentication.
455 NTSTATUS gensec_session_info(struct gensec_security *gensec_security,
456 struct auth_session_info **session_info)
458 if (!gensec_security->ops->session_info) {
459 return NT_STATUS_NOT_IMPLEMENTED;
461 return gensec_security->ops->session_info(gensec_security, session_info);
465 * Next state function for the GENSEC state machine
467 * @param gensec_security GENSEC State
468 * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
469 * @param in The request, as a DATA_BLOB
470 * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
471 * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent,
472 * or NT_STATUS_OK if the user is authenticated.
475 NTSTATUS gensec_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
476 const DATA_BLOB in, DATA_BLOB *out)
478 return gensec_security->ops->update(gensec_security, out_mem_ctx, in, out);
482 * Set the requirement for a certain feature on the connection
486 void gensec_want_feature(struct gensec_security *gensec_security,
489 gensec_security->want_features |= feature;
493 * Check the requirement for a certain feature on the connection
497 BOOL gensec_have_feature(struct gensec_security *gensec_security,
500 if (!gensec_security->ops->have_feature) {
503 return gensec_security->ops->have_feature(gensec_security, feature);
507 * Associate a credentails structure with a GENSEC context - talloc_reference()s it to the context
511 NTSTATUS gensec_set_credentials(struct gensec_security *gensec_security, struct cli_credentials *credentials)
513 gensec_security->credentials = talloc_reference(gensec_security, credentials);
518 * Return the credentails structure associated with a GENSEC context
522 struct cli_credentials *gensec_get_credentials(struct gensec_security *gensec_security)
524 return gensec_security->credentials;
528 * Set the target service (such as 'http' or 'host') on a GENSEC context - ensures it is talloc()ed
532 NTSTATUS gensec_set_target_service(struct gensec_security *gensec_security, const char *service)
534 gensec_security->target.service = talloc_strdup(gensec_security, service);
535 if (!gensec_security->target.service) {
536 return NT_STATUS_NO_MEMORY;
542 * Set the target hostname (suitable for kerberos resolutation) on a GENSEC context - ensures it is talloc()ed
546 NTSTATUS gensec_set_target_hostname(struct gensec_security *gensec_security, const char *hostname)
548 gensec_security->target.hostname = talloc_strdup(gensec_security, hostname);
549 if (!gensec_security->target.hostname) {
550 return NT_STATUS_NO_MEMORY;
555 const char *gensec_get_target_hostname(struct gensec_security *gensec_security)
557 if (gensec_security->target.hostname) {
558 return gensec_security->target.hostname;
561 /* TODO: Add a 'set sockaddr' call, and do a reverse lookup */
565 const char *gensec_get_target_service(struct gensec_security *gensec_security)
567 if (gensec_security->target.service) {
568 return gensec_security->target.service;
575 register a GENSEC backend.
577 The 'name' can be later used by other backends to find the operations
578 structure for this backend.
580 NTSTATUS gensec_register(const void *_ops)
582 const struct gensec_security_ops *ops = _ops;
584 if (!lp_parm_bool(-1, "gensec", ops->name, ops->enabled)) {
585 DEBUG(2,("gensec subsystem %s is disabled\n", ops->name));
589 if (gensec_security_by_name(ops->name) != NULL) {
590 /* its already registered! */
591 DEBUG(0,("GENSEC backend '%s' already registered\n",
593 return NT_STATUS_OBJECT_NAME_COLLISION;
596 generic_security_ops = realloc_p(generic_security_ops,
597 const struct gensec_security_ops *,
598 gensec_num_backends+1);
599 if (!generic_security_ops) {
600 smb_panic("out of memory in gensec_register");
603 generic_security_ops[gensec_num_backends] = ops;
605 gensec_num_backends++;
607 DEBUG(3,("GENSEC backend '%s' registered\n",
614 return the GENSEC interface version, and the size of some critical types
615 This can be used by backends to either detect compilation errors, or provide
616 multiple implementations for different smbd compilation options in one module
618 const struct gensec_critical_sizes *gensec_interface_version(void)
620 static const struct gensec_critical_sizes critical_sizes = {
621 GENSEC_INTERFACE_VERSION,
622 sizeof(struct gensec_security_ops),
623 sizeof(struct gensec_security),
626 return &critical_sizes;
630 initialise the GENSEC subsystem
632 NTSTATUS gensec_init(void)