2 Unix SMB/CIFS implementation.
4 Generic Authentication Interface
6 Copyright (C) Andrew Tridgell 2003
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
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_p(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 note that memory context is the parent context to hang this gensec context off. It may be NULL.
121 static NTSTATUS gensec_start(TALLOC_CTX *mem_ctx, struct gensec_security **gensec_security)
123 (*gensec_security) = talloc_p(mem_ctx, struct gensec_security);
124 if (!(*gensec_security)) {
125 return NT_STATUS_NO_MEMORY;
128 (*gensec_security)->ops = NULL;
130 ZERO_STRUCT((*gensec_security)->user);
131 ZERO_STRUCT((*gensec_security)->target);
132 ZERO_STRUCT((*gensec_security)->default_user);
134 (*gensec_security)->default_user.name = "";
135 (*gensec_security)->default_user.domain = talloc_strdup(*gensec_security, lp_workgroup());
136 (*gensec_security)->default_user.realm = talloc_strdup(*gensec_security, lp_realm());
138 (*gensec_security)->subcontext = False;
139 (*gensec_security)->want_features = 0;
140 (*gensec_security)->have_features = 0;
145 * Start a GENSEC subcontext, with a copy of the properties of the parent
147 * @note Used by SPENGO in particular, for the actual implementation mechanism
150 NTSTATUS gensec_subcontext_start(TALLOC_CTX *mem_ctx,
151 struct gensec_security *parent,
152 struct gensec_security **gensec_security)
154 (*gensec_security) = talloc_p(mem_ctx, struct gensec_security);
155 if (!(*gensec_security)) {
156 return NT_STATUS_NO_MEMORY;
159 (**gensec_security) = *parent;
160 (*gensec_security)->ops = NULL;
161 (*gensec_security)->private_data = NULL;
163 (*gensec_security)->subcontext = True;
168 NTSTATUS gensec_client_start(TALLOC_CTX *mem_ctx, struct gensec_security **gensec_security)
171 status = gensec_start(mem_ctx, gensec_security);
172 if (!NT_STATUS_IS_OK(status)) {
175 (*gensec_security)->gensec_role = GENSEC_CLIENT;
176 (*gensec_security)->password_callback = NULL;
178 ZERO_STRUCT((*gensec_security)->user);
183 NTSTATUS gensec_server_start(TALLOC_CTX *mem_ctx, struct gensec_security **gensec_security)
186 status = gensec_start(mem_ctx, gensec_security);
187 if (!NT_STATUS_IS_OK(status)) {
190 (*gensec_security)->gensec_role = GENSEC_SERVER;
195 static NTSTATUS gensec_start_mech(struct gensec_security *gensec_security)
198 DEBUG(5, ("Starting GENSEC %smechanism %s\n",
199 gensec_security->subcontext ? "sub" : "",
200 gensec_security->ops->name));
201 switch (gensec_security->gensec_role) {
203 if (gensec_security->ops->client_start) {
204 status = gensec_security->ops->client_start(gensec_security);
205 if (!NT_STATUS_IS_OK(status)) {
206 DEBUG(1, ("Failed to start GENSEC client mech %s: %s\n",
207 gensec_security->ops->name, nt_errstr(status)));
212 if (gensec_security->ops->server_start) {
213 status = gensec_security->ops->server_start(gensec_security);
214 if (!NT_STATUS_IS_OK(status)) {
215 DEBUG(1, ("Failed to start GENSEC server mech %s: %s\n",
216 gensec_security->ops->name, nt_errstr(status)));
221 return NT_STATUS_INVALID_PARAMETER;
225 * Start a GENSEC sub-mechanism by DCERPC allocated 'auth type' number
228 NTSTATUS gensec_start_mech_by_authtype(struct gensec_security *gensec_security,
229 uint8_t auth_type, uint8_t auth_level)
231 gensec_security->ops = gensec_security_by_authtype(auth_type);
232 if (!gensec_security->ops) {
233 DEBUG(3, ("Could not find GENSEC backend for auth_type=%d\n", (int)auth_type));
234 return NT_STATUS_INVALID_PARAMETER;
236 gensec_want_feature(gensec_security, GENSEC_FEATURE_DCE_STYLE);
237 if (auth_level == DCERPC_AUTH_LEVEL_INTEGRITY) {
238 gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN);
240 if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
241 gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN);
242 gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL);
245 return gensec_start_mech(gensec_security);
248 const char *gensec_get_name_by_authtype(uint8_t authtype)
250 const struct gensec_security_ops *ops;
251 ops = gensec_security_by_authtype(authtype);
259 const char *gensec_get_name_by_oid(const char *oid_string)
261 const struct gensec_security_ops *ops;
262 ops = gensec_security_by_oid(oid_string);
271 * Start a GENSEC sub-mechanism by OID, used in SPNEGO
273 * @note This should also be used when you wish to just start NLTMSSP (for example), as it uses a
274 * well-known #define to hook it in.
277 NTSTATUS gensec_start_mech_by_oid(struct gensec_security *gensec_security,
278 const char *mech_oid)
280 gensec_security->ops = gensec_security_by_oid(mech_oid);
281 if (!gensec_security->ops) {
282 DEBUG(3, ("Could not find GENSEC backend for oid=%s\n", mech_oid));
283 return NT_STATUS_INVALID_PARAMETER;
285 return gensec_start_mech(gensec_security);
289 * Start a GENSEC sub-mechanism by a well know SASL name
293 NTSTATUS gensec_start_mech_by_sasl_name(struct gensec_security *gensec_security,
294 const char *sasl_name)
296 gensec_security->ops = gensec_security_by_sasl_name(sasl_name);
297 if (!gensec_security->ops) {
298 DEBUG(3, ("Could not find GENSEC backend for sasl_name=%s\n", sasl_name));
299 return NT_STATUS_INVALID_PARAMETER;
301 return gensec_start_mech(gensec_security);
305 wrappers for the gensec function pointers
307 NTSTATUS gensec_unseal_packet(struct gensec_security *gensec_security,
309 uint8_t *data, size_t length,
310 const uint8_t *whole_pdu, size_t pdu_length,
313 if (!gensec_security->ops->unseal_packet) {
314 return NT_STATUS_NOT_IMPLEMENTED;
316 if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
317 if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
318 return gensec_check_packet(gensec_security, mem_ctx,
320 whole_pdu, pdu_length,
323 return NT_STATUS_INVALID_PARAMETER;
326 return gensec_security->ops->unseal_packet(gensec_security, mem_ctx,
328 whole_pdu, pdu_length,
332 NTSTATUS gensec_check_packet(struct gensec_security *gensec_security,
334 const uint8_t *data, size_t length,
335 const uint8_t *whole_pdu, size_t pdu_length,
336 const DATA_BLOB *sig)
338 if (!gensec_security->ops->check_packet) {
339 return NT_STATUS_NOT_IMPLEMENTED;
341 if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
342 return NT_STATUS_INVALID_PARAMETER;
345 return gensec_security->ops->check_packet(gensec_security, mem_ctx, data, length, whole_pdu, pdu_length, sig);
348 NTSTATUS gensec_seal_packet(struct gensec_security *gensec_security,
350 uint8_t *data, size_t length,
351 const uint8_t *whole_pdu, size_t pdu_length,
354 if (!gensec_security->ops->seal_packet) {
355 return NT_STATUS_NOT_IMPLEMENTED;
357 if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
358 if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
359 return gensec_sign_packet(gensec_security, mem_ctx,
361 whole_pdu, pdu_length,
364 return NT_STATUS_INVALID_PARAMETER;
367 return gensec_security->ops->seal_packet(gensec_security, mem_ctx, data, length, whole_pdu, pdu_length, sig);
370 NTSTATUS gensec_sign_packet(struct gensec_security *gensec_security,
372 const uint8_t *data, size_t length,
373 const uint8_t *whole_pdu, size_t pdu_length,
376 if (!gensec_security->ops->sign_packet) {
377 return NT_STATUS_NOT_IMPLEMENTED;
379 if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
380 return NT_STATUS_INVALID_PARAMETER;
383 return gensec_security->ops->sign_packet(gensec_security, mem_ctx, data, length, whole_pdu, pdu_length, sig);
386 size_t gensec_sig_size(struct gensec_security *gensec_security)
388 if (!gensec_security->ops->sig_size) {
391 if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
395 return gensec_security->ops->sig_size(gensec_security);
398 NTSTATUS gensec_session_key(struct gensec_security *gensec_security,
399 DATA_BLOB *session_key)
401 if (!gensec_security->ops->session_key) {
402 return NT_STATUS_NOT_IMPLEMENTED;
404 return gensec_security->ops->session_key(gensec_security, session_key);
408 * Return the credentials of a logged on user, including session keys
411 * Only valid after a successful authentication
413 * May only be called once per authentication.
417 NTSTATUS gensec_session_info(struct gensec_security *gensec_security,
418 struct auth_session_info **session_info)
420 if (!gensec_security->ops->session_info) {
421 return NT_STATUS_NOT_IMPLEMENTED;
423 return gensec_security->ops->session_info(gensec_security, session_info);
427 * Next state function for the GENSEC state machine
429 * @param gensec_security GENSEC State
430 * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
431 * @param in The request, as a DATA_BLOB
432 * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
433 * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent,
434 * or NT_STATUS_OK if the user is authenticated.
437 NTSTATUS gensec_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
438 const DATA_BLOB in, DATA_BLOB *out)
440 return gensec_security->ops->update(gensec_security, out_mem_ctx, in, out);
444 * Set the requirement for a certain feature on the connection
448 void gensec_want_feature(struct gensec_security *gensec_security,
451 gensec_security->want_features |= feature;
455 * Check the requirement for a certain feature on the connection
459 BOOL gensec_have_feature(struct gensec_security *gensec_security,
462 if (gensec_security->have_features & feature) {
470 * Set a username on a GENSEC context - ensures it is talloc()ed
474 NTSTATUS gensec_set_unparsed_username(struct gensec_security *gensec_security, const char *user)
477 char *u = talloc_strdup(gensec_security, user);
479 return NT_STATUS_NO_MEMORY;
482 p = strchr_m(user, '@');
486 gensec_security->user.name = talloc_strdup(gensec_security, u);
487 if (!gensec_security->user.name) {
488 return NT_STATUS_NO_MEMORY;
491 gensec_security->user.realm = talloc_strdup(gensec_security, p+1);
492 if (!gensec_security->user.realm) {
493 return NT_STATUS_NO_MEMORY;
498 p = strchr_m(user, '\\');
500 p = strchr_m(user, '/');
505 gensec_security->user.domain = talloc_strdup(gensec_security, u);
506 if (!gensec_security->user.domain) {
507 return NT_STATUS_NO_MEMORY;
509 gensec_security->user.name = talloc_strdup(gensec_security, p+1);
510 if (!gensec_security->user.name) {
511 return NT_STATUS_NO_MEMORY;
517 gensec_security->user.name = u;
518 if (!gensec_security->user.name) {
519 return NT_STATUS_NO_MEMORY;
525 * Set a username on a GENSEC context - ensures it is talloc()ed
529 NTSTATUS gensec_set_username(struct gensec_security *gensec_security, const char *user)
531 gensec_security->user.name = talloc_strdup(gensec_security, user);
532 if (!gensec_security->user.name) {
533 return NT_STATUS_NO_MEMORY;
539 * Set a username on a GENSEC context - ensures it is talloc()ed
543 const char *gensec_get_username(struct gensec_security *gensec_security)
545 if (gensec_security->user.name) {
546 return gensec_security->user.name;
548 return gensec_security->default_user.name;
552 * Set a domain on a GENSEC context - ensures it is talloc()ed
556 NTSTATUS gensec_set_domain(struct gensec_security *gensec_security, const char *domain)
558 gensec_security->user.domain = talloc_strdup(gensec_security, domain);
559 if (!gensec_security->user.domain) {
560 return NT_STATUS_NO_MEMORY;
566 * Return the NT domain for this GENSEC context
570 const char *gensec_get_domain(struct gensec_security *gensec_security)
572 if (gensec_security->user.domain) {
573 return gensec_security->user.domain;
574 } else if (gensec_security->user.realm) {
575 return gensec_security->user.realm;
577 return gensec_security->default_user.domain;
581 * Set a kerberos realm on a GENSEC context - ensures it is talloc()ed
585 NTSTATUS gensec_set_realm(struct gensec_security *gensec_security, const char *realm)
587 gensec_security->user.realm = talloc_strdup(gensec_security, realm);
588 if (!gensec_security->user.realm) {
589 return NT_STATUS_NO_MEMORY;
595 * Return the Krb5 realm for this context
599 const char *gensec_get_realm(struct gensec_security *gensec_security)
601 if (gensec_security->user.realm) {
602 return gensec_security->user.realm;
603 } else if (gensec_security->user.domain) {
604 return gensec_security->user.domain;
606 return gensec_security->default_user.realm;
610 * Return a kerberos principal for this context, if one has been set
614 char *gensec_get_client_principal(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx)
616 const char *realm = gensec_get_realm(gensec_security);
618 return talloc_asprintf(mem_ctx, "%s@%s",
619 gensec_get_username(gensec_security),
620 gensec_get_realm(gensec_security));
622 return talloc_strdup(mem_ctx, gensec_get_username(gensec_security));
627 * Set the password outright on GENSEC context - ensures it is talloc()ed, and that we will
632 NTSTATUS gensec_set_password(struct gensec_security *gensec_security,
633 const char *password)
635 gensec_security->user.password = talloc_strdup(gensec_security, password);
636 if (password && !gensec_security->user.password) {
637 return NT_STATUS_NO_MEMORY;
643 * Set the target principal name (if already known) on a GENSEC context - ensures it is talloc()ed
647 NTSTATUS gensec_set_target_principal(struct gensec_security *gensec_security, const char *principal)
649 gensec_security->target.principal = talloc_strdup(gensec_security, principal);
650 if (!gensec_security->target.principal) {
651 return NT_STATUS_NO_MEMORY;
657 * Set the target service (such as 'http' or 'host') on a GENSEC context - ensures it is talloc()ed
661 NTSTATUS gensec_set_target_service(struct gensec_security *gensec_security, const char *service)
663 gensec_security->target.service = talloc_strdup(gensec_security, service);
664 if (!gensec_security->target.service) {
665 return NT_STATUS_NO_MEMORY;
671 * Set the target hostname (suitable for kerberos resolutation) on a GENSEC context - ensures it is talloc()ed
675 NTSTATUS gensec_set_target_hostname(struct gensec_security *gensec_security, const char *hostname)
677 gensec_security->target.hostname = talloc_strdup(gensec_security, hostname);
678 if (!gensec_security->target.hostname) {
679 return NT_STATUS_NO_MEMORY;
684 const char *gensec_get_target_hostname(struct gensec_security *gensec_security)
686 if (gensec_security->target.hostname) {
687 return gensec_security->target.hostname;
690 /* TODO: Add a 'set sockaddr' call, and do a reverse lookup */
694 const char *gensec_get_target_service(struct gensec_security *gensec_security)
696 if (gensec_security->target.service) {
697 return gensec_security->target.service;
703 const char *gensec_get_target_principal(struct gensec_security *gensec_security)
705 const char *mechListMIC;
707 if (gensec_security->target.principal) {
708 return gensec_security->target.principal;
711 mechListMIC = talloc_asprintf(gensec_security,"%s$@%s",
718 * Set a password callback, if the gensec module we use demands a password
721 void gensec_set_password_callback(struct gensec_security *gensec_security,
722 gensec_password_callback callback, void *callback_private_data)
724 gensec_security->password_callback = callback;
725 gensec_security->password_callback_private = callback_private_data;
729 * Get (or call back for) a password.
732 NTSTATUS gensec_get_password(struct gensec_security *gensec_security,
736 if (gensec_security->user.password) {
737 *password = talloc_strdup(mem_ctx, gensec_security->user.password);
739 return NT_STATUS_NO_MEMORY;
744 if (!gensec_security->password_callback) {
748 return gensec_security->password_callback(gensec_security, mem_ctx, password);
752 register a GENSEC backend.
754 The 'name' can be later used by other backends to find the operations
755 structure for this backend.
757 NTSTATUS gensec_register(const void *_ops)
759 const struct gensec_security_ops *ops = _ops;
761 if (!lp_parm_bool(-1, "gensec", ops->name, True)) {
762 DEBUG(2,("gensec subsystem %s is disabled\n", ops->name));
766 if (gensec_security_by_name(ops->name) != NULL) {
767 /* its already registered! */
768 DEBUG(0,("GENSEC backend '%s' already registered\n",
770 return NT_STATUS_OBJECT_NAME_COLLISION;
773 generic_security_ops = realloc_p(generic_security_ops,
774 const struct gensec_security_ops *,
775 gensec_num_backends+1);
776 if (!generic_security_ops) {
777 smb_panic("out of memory in gensec_register");
780 generic_security_ops[gensec_num_backends] = ops;
782 gensec_num_backends++;
784 DEBUG(3,("GENSEC backend '%s' registered\n",
791 return the GENSEC interface version, and the size of some critical types
792 This can be used by backends to either detect compilation errors, or provide
793 multiple implementations for different smbd compilation options in one module
795 const struct gensec_critical_sizes *gensec_interface_version(void)
797 static const struct gensec_critical_sizes critical_sizes = {
798 GENSEC_INTERFACE_VERSION,
799 sizeof(struct gensec_security_ops),
800 sizeof(struct gensec_security),
803 return &critical_sizes;
807 initialise the GENSEC subsystem
809 NTSTATUS gensec_init(void)
811 gensec_dcerpc_schannel_init();