summaryrefslogtreecommitdiffstats
path: root/source4/auth/kerberos
diff options
context:
space:
mode:
Diffstat (limited to 'source4/auth/kerberos')
-rw-r--r--source4/auth/kerberos/kerberos-notes.txt760
-rw-r--r--source4/auth/kerberos/kerberos-porting-to-mit-notes.txt803
-rw-r--r--source4/auth/kerberos/kerberos.h89
-rw-r--r--source4/auth/kerberos/kerberos_credentials.h38
-rw-r--r--source4/auth/kerberos/kerberos_pac.c539
-rw-r--r--source4/auth/kerberos/kerberos_util.c721
-rw-r--r--source4/auth/kerberos/krb5_init_context.c885
-rw-r--r--source4/auth/kerberos/krb5_init_context.h79
-rw-r--r--source4/auth/kerberos/srv_keytab.c407
-rw-r--r--source4/auth/kerberos/wscript_build26
10 files changed, 4347 insertions, 0 deletions
diff --git a/source4/auth/kerberos/kerberos-notes.txt b/source4/auth/kerberos/kerberos-notes.txt
new file mode 100644
index 0000000..cb8f0a9
--- /dev/null
+++ b/source4/auth/kerberos/kerberos-notes.txt
@@ -0,0 +1,760 @@
+Copyright Andrew Bartlett <abartlet@samba.org> 2005-2009
+Copyright Donald T. Davis <don@mit.edu>
+
+Released under the GPLv3
+
+Important context for porting to MIT
+------------------------------------
+
+This document should be read in conjunction with the Samba4 source code.
+DAL and KDC requirements are expressed (as an implementation against Heimdal's
+HDB abstraction layer) in Samba4's source4/kdc/hdb-samba4.c in particular.
+hbd-samba4.c is the biggest piece of samba-to-krb glue layer, so the main
+part of the port to MIT is to replace hdb-samba4 with a similar glue layer
+that's designed for MIT's code.
+
+PAC requirements are implemented in source4/kdc/pac-glue.c
+
+The plugins (both of the above are Heimdal plugins) for the above are loaded
+in source4/kdc/kdc.c
+
+For GSSAPI requirements, see auth/gensec/gensec_gssapi.c (the consumer of
+GSSAPI in Samba4)
+
+For Kerberos requirements, see auth/kerberos/krb5_init_context.c .
+
+Samba has its own credentials system, wrapping GSS creds, just as GSS
+creds wrap around krb5 creds. For the interaction between Samba4 credentials
+system and GSSAPI and Kerberos see auth/credentials/credentials_krb5.c .
+
+AllowedWorkstationNames and Krb5
+--------------------------------
+
+Microsoft uses the clientAddresses *multiple value* field in the krb5
+protocol (particularly the AS_REQ) to communicate the client's netbios
+name (legacy undotted name, <14 chars)
+
+This is (my guess) to support the userWorkstations field (in user's AD record).
+The idea is to support client-address restrictions, as was standard in NT:
+The AD authentication server I imagine checks the netbios address against
+this userWorkstations value (BTW, the NetLogon server does this, too).
+
+The checking of this field implies a little of the next question:
+
+Is a DAL the layer we need?
+---------------------------
+
+Looking at what we need to pass around, I don't think
+the DAL is even the right layer; what we really want
+is to create an account-authorization abstraction layer
+(e.g., is this account permitted to login to this computer,
+at this time?).
+Here is how we ended up doing this in Heimdal:
+ * We created a separate plugin, with this API:
+ typedef struct hdb_entry_ex {
+ void *ctx;
+ hdb_entry entry;
+ void (*free_entry)(krb5_context, struct hdb_entry_ex *);
+ } hdb_entry_ex;
+
+ * The void *ctx is a "private pointer," provided by the 'get' method's
+ hdb_entry_ex retval. The APIs below use the void *ctx so as to find
+ additional information about the user, not contained in the hdb_entry
+ structure. Both the provider and the APIs below understand how to cast
+ the private void *ctx pointer.
+
+ typedef krb5_error_code
+ (*krb5plugin_windc_pac_generate)(void *, krb5_context,
+ struct hdb_entry_ex *, krb5_pac*);
+ typedef krb5_error_code
+ (*krb5plugin_windc_pac_verify)(void *, krb5_context,
+ const krb5_principal,
+ struct hdb_entry_ex *,
+ struct hdb_entry_ex *,
+ krb5_pac *);
+ typedef krb5_error_code
+ (*krb5plugin_windc_client_access)(void *,
+ krb5_context,
+ struct hdb_entry_ex *,
+ KDC_REQ *, krb5_data *);
+
+ * (The krb5_data* here is critical, so that samba's KDC can return
+ the right NTSTATUS code in the 'error string' returned to the client.
+ Otherwise, the windows client won't get the right error message to
+ the user (such as 'password expired' etc). The pure Kerberos error
+ is not enough)
+
+ typedef struct krb5plugin_windc_ftable {
+ int minor_version;
+ krb5_error_code (*init)(krb5_context, void **);
+ void (*fini)(void *);
+ rb5plugin_windc_pac_generate pac_generate;
+ krb5plugin_windc_pac_verify pac_verify;
+ krb5plugin_windc_client_access client_access;
+ } krb5plugin_windc_ftable;
+ This API has some heimdal-specific stuff, that'll have to change when we port the plugin to MIT krb.
+ * 1st callback (pac_generate) creates an initial PAC from the user's AD record.
+ * 2nd callback (pac_verify) check that a PAC is correctly signed, add additional groups (for cross-realm tickets) and re-sign with the key of the target kerberos service's account
+ * 3rd callback (client_access) perform additional access checks, such as allowedWorkstations and account expiry.
+ * for example, to register this plugin, use the kdc's standard
+ plugin-system at Samba4's initialisation:
+ /* first, setup the table of callback pointers */
+ /* Registar WinDC hooks */
+ ret = krb5_plugin_register(krb5_context,
+ PLUGIN_TYPE_DATA, "windc",
+ &windc_plugin_table);
+ /* once registered, the KDC will invoke the callbacks */
+ /* while preparing each new ticket (TGT or app-tkt) */
+ * an alternate way to register the plugin is with a config-file that names
+ a DSO (Dynamically Shared Object).
+
+
+This plugin helps bridge an important gap: The user's AD record is much
+richer than the Heimdal HDB format allows, so we do AD-specific access
+control checks in an AD-specific layer (ie, the plugin), not in the
+DB-agnostic KDC server.
+
+In Novell's pure DAL approach, the DAL only read in the principalName as
+the key, so it had trouble performing access-control decisions on things
+other than the name (like the addresses).
+
+There is another, currently unhandled challenge in this area - the need to handle
+bad password counts (and good password notification), so that a single policy can
+be applied against all means of checking a password (NTLM, Kerberos, LDAP Simple
+bind etc)
+
+The Original work by Novell in creating a DAL did not seem to provide a way to
+update the PW counts information. Nevertheless, we know that this is very much
+required (and may have been addressed in Simo's subsequent IPA-KDC design),
+because in Samba3+eDirectory, great lengths are taken to update this information.
+
+GSSAPI layer requirements
+-------------------------
+
+Welcome to the wonderful world of canonicalisation
+
+The MIT Krb5 libs (including GSSAPI) do not support kinit returning a different
+realm to what the client asked for, even just in case differences.
+
+Heimdal has the same problem, and this too applies to the krb5 layer, not
+just gssapi.
+
+there's two kinds of name-canonicalization that can occur:
+ * lower-to-upper case conversion, because Windows domain names are
+ usually in upper case;
+ * an unrecognizable substitution of names, such as might happen when
+ a user requests a ticket for a NetBIOS domain name, but gets back
+ a ticket for the corresponding FQDN.
+
+As developers, we should test if the AD KDC's name-canonicalisation
+can be turned off with the KDCOption flags in the AS-REQ or TGS-REQ;
+Windows clients always send the Canonicalize flags as KDCOption values.
+
+Old Clients (samba3 and HPUX clients) use 'selfmade' gssapi/krb5 tokens
+for use in the CIFS session setup. these hand-crafted ASN.1 packets don't
+follow rfc1964 perfectly, so server-side krblib code has to be flexible
+enough to accept these bent tokens.
+It turns out that Windows' GSSAPI server-side code is sloppy about checking
+some GSSAPI tokens' checksums. During initial work to implement an AD client,
+it was easier to make an acceptable solution (to Windows servers) than to
+correctly implement the GSSAPI specification, particularly on top of the
+(inflexible) MIT Kerberos API. It did not seem possible to write a correct,
+separate GSSAPI implementation on top of MIT Kerberos's public krb5lib API,
+and at the time, the effort did not need to extend beyond what Windows would
+require.
+
+The upshot is that old Samba3 clients send GSSAPI tokens bearing incorrect
+checksums, which AD's Krb5lib cheerfully accepts (but accepts the good checksums,
+too). Similarly, Samba4's heimdal krb5lib accepts these incorrect checksums.
+Accordingly, if MIT's krb5lib wants to interoperate with the old Samba3 clients,
+then MIT's library will have to do the same.
+
+Because these old clients use krb5_mk_req()
+the app-servers get a chksum field depending on the encryption type, but that's
+wrong for GSSAPI (see rfc 1964 section 1.1.1). The Checksum type 8003 should
+be used in the Authenticator of the AP-REQ! That (correct use of the 8003 type)
+would allows the channel bindings, the GCC_C_* req_flags and optional delegation
+tickets to be passed from the client to the server. However windows doesn't
+seem to care whether the checksum is of the wrong type, and for CIFS SessionSetups,
+it seems that the req_flags are just set to 0.
+This deviant checksum can't work for LDAP connections with sign or seal, or
+for any DCERPC connection, because those connections do not require the
+negotiation of GSS-Wrap paraemters (signing or sealing of whole payloads).
+Note: CIFS has an independent SMB signing mechanism, using the Kerberos key.
+
+see heimdal/lib/gssapi/krb5/accept_sec_context.c, lines 390-450 or so.
+
+This bug-compatibility is likely to be controversial in the kerberos community,
+but a similar need for bug-compatibility arose around MIT's & Heimdal's both
+failing to support TGS_SUBKEYs correctly, and there are numerous other cases.
+see https://lists.anl.gov/pipermail/ietf-krb-wg/2009-May/007630.html
+
+So MIT's krb5lib needs to also support old clients!
+
+Principal Names, long and short names
+-------------------------------------
+
+As far as servicePrincipalNames are concerned, these are not
+canonicalised by AD's KDC, except as regards the realm in the reply.
+That is, the client gets back the principal it asked for, with
+the realm portion 'fixed' to uppercase, long form.
+Heimdal doesn't canonicalize names, but Samba4 does some canonicalization:
+For hostnames and usernames, Samba4 canonicalizes the requested name only
+for the LDAP principal-lookup, but then Samba4 returns the retrieved LDAP
+record with the request's original, uncanonicalized hostname replacing the
+canonicalized name that actually was retrieved.
+AB says that for usernames, Samba4 used to return the canonicalized username,
+as retrieved from LDAP. The reason for the different treatment was that
+the user needs to present his own canonicalized username to servers, for
+ACL-matching. For hostnames this isn't necessary.
+So, for bug-compatibility, we may need to optionally disable any
+namne-canonicalization that MIT's KDC does.
+
+The short name of the realm seems to be accepted for at least AS_REQ
+operations, but the AD KDC always performs realm-canonicalisation,
+which converts the short realm-name to the canonical long form.
+So, this causes pain for current krb client libraries.
+
+The canonicalisation of names matters not only for the KDC, but also
+for code that has to deal with keytabs.
+With credential-caches, when canonicalization leads to cache-misses,
+the client just asks for new credentials for the variant server-name.
+This could happen, for example, if the user asks to access the server
+twice, using different variants of the server-name.
+
+We also need to handle type 10 names (NT-ENTERPRISE), which are a full
+principal name in the principal field, unrelated to the realm.
+The principal field contains both principal & realm names, while the
+realm field contains a realm name, too, possibly different.
+For example, an NT-ENTERPRISE principal name might look like:
+joeblow@microsoft.com@NTDEV.MICROSOFT.COM ,
+<--principal field-->|<----realm name--->|
+
+Where joe@microsoft.com is the leading portion, and NTDEV.MICROSOFT.COM is
+the realm. This is used for the 'email address-like login-name' feature of AD.
+
+HOST/ Aliases
+-------------
+
+There is another post somewhere (ref lost for the moment) that details
+where in active directory the list of stored aliases for HOST/ is.
+This list is read & parsed by the AD KDC, so as to allow any of these
+aliased ticket-requests to use the HOST/ key.
+
+Samba4 currently has set:
+sPNMappings: host=ldap,dns,cifs,http (but dns's presence is a bug, somehow)
+
+AD actually has ~50 entries:
+
+sPNMappings: host=alerter,appmgmt,cisvc,clipsrv,browser,dhcp,dnscache,replicat
+ or,eventlog,eventsystem,policyagent,oakley,dmserver,dns,mcsvc,fax,msiserver,i
+ as,messenger,netlogon,netman,netdde,netddedsm,nmagent,plugplay,protectedstora
+ ge,rasman,rpclocator,rpc,rpcss,remoteaccess,rsvp,samss,scardsvr,scesrv,seclog
+ on,scm,dcom,cifs,spooler,snmp,schedule,tapisrv,trksvr,trkwks,ups,time,wins,ww
+ w,http,w3svc,iisadmin,msdtc
+
+Domain members that expect the longer list will break in damb4, as of 6/09.
+AB says he'll try to fix this right away.
+
+For example, this is how HTTP/, and CIFS/ can use HOST/ without
+any explicit entry in the servicePrincipalName attribute
+
+
+For example, the application-server might have (on its AD record):
+servicePrincipalName: HOST/my.computer@MY.REALM
+
+but the client asks for a ticket to cifs/my.computer@MY.REALM
+AD looks in LDAP for both name-variants
+AD then transposes cifs -> host after performing the lookup in the
+directory (for the original name), then looks for host/my.computer@MY.REALM
+
+for hostnames & usernames, alternate names appear as extra values in
+the multivalued "principal name" attributes:
+ - For hostnames, the other names (other than it's short name, implied
+ from the CN), is stored in the servicePrincipalName
+ - For usernames, the other names are stored in the userPrincipalName
+ attribute, and can be full e-mail address like names, such as
+ joe@microsoft.com (see above).
+
+Jean-Baptiste.Marchand@hsc.fr reminds me:
+> This is the SPNMappings attribute in Active Directory:
+> http://msdn.microsoft.com/library/en-us/adschema/adschema/a_spnmappings.asp
+
+We implement this in hdb-ldb.
+
+Implicit names for Win2000 Accounts
+-----------------------------------
+AD's records for servers are keyed by CN or by servicePrincipalName,
+but for win2k boxes, these records don't include servicePrincipalName,
+so, the CN attribute is used instead.
+Despite not having a servicePrincipalName on accounts created
+by computers running win2000, it appears we are expected
+to have an implicit mapping from host/computer.full.name and
+host/computer to the computer's entry in the AD LDAP database
+(ie, be able to obtain tickets for that host name in the KDC).
+
+Returned Salt for PreAuthentication
+-----------------------------------
+
+When the KDC replies for pre-authentication, it returns the Salt,
+which may be in the form of a principalName that is in no way
+connected with the current names. (ie, even if the userPrincipalName
+and samAccountName are renamed, the old salt is returned).
+
+This is the kerberos standard salt, kept in the 'Key'. The
+AD generation rules are found in a Mail from Luke Howard dated
+10 Nov 2004. The MIT glue layer doesn't really need to care about
+these salt-handling details; the samba4 code & the LDAP backend
+will conspire to make sure that MIT's KDC gets correct salts.
+
+
+From: Luke Howard <lukeh@padl.com>
+Organization: PADL Software Pty Ltd
+To: lukeh@padl.com
+Date: Wed, 10 Nov 2004 13:31:21 +1100
+Cc: huaraz@moeller.plus.com, samba-technical@lists.samba.org
+Subject: Re: Samba-3.0.7-1.3E Active Directory Issues
+-------
+
+Did some more testing, it appears the behaviour has another
+explanation. It appears that the standard Kerberos password salt
+algorithm is applied in Windows 2003, just that the source principal
+name is different.
+
+Here is what I've been able to deduce from creating a bunch of
+different accounts:
+[SAM name in this mail means the AD attribute samAccountName .
+ E.g., jbob for a user and jbcomputer$ for a computer.]
+
+[UPN is the AD userPrincipalName attribute. For example, jbob@mydomain.com]
+
+Type of account Principal for Salting
+========================================================================
+Computer Account host/<SAM-Name-Without-$>.realm@REALM
+User Account Without UPN <SAM-Name>@REALM
+User Account With UPN <LHS-Of-UPN>@REALM
+
+Note that if the computer account's SAM account name does not include
+the trailing '$', then the entire SAM account name is used as input to
+the salting principal. Setting a UPN for a computer account has no
+effect.
+
+It seems to me odd that the RHS of the UPN is not used in the salting
+principal. For example, a user with UPN foo@mydomain.com in the realm
+MYREALM.COM would have a salt of MYREALM.COMfoo. Perhaps this is to
+allow a user's UPN suffix to be changed without changing the salt. And
+perhaps using the UPN for salting signifies a move away SAM names and
+their associated constraints.
+
+For more information on how UPNs relate to the Kerberos protocol,
+see:
+
+http://www.ietf.org/proceedings/01dec/I-D/draft-ietf-krb-wg-kerberos-referrals-02.txt
+
+-- Luke
+
+
+
+Heimdal oddities
+----------------
+
+Heimdal is built such that it should be able to serve multiple realms
+at the same time. This isn't relevant for Samba's use, but it shows
+up in a lot of generalisations throughout the code.
+
+Samba4's code originally tried internally to make it possible to use
+Heimdal's multi-realms-per-KDC ability, but this was ill-conceived,
+and AB has recently (6/09) ripped the last of that multi-realms
+stuff out of samba4. AB says that in AD, it's not really possible
+to make this work; several AD components structurally assume that
+there's one realm per KDC. However, we do use this to support
+canonicalization of realm-names: case variations, plus long-vs-short
+variants of realm-names.
+
+Other odd things:
+ - Heimdal supports multiple passwords on a client account: Samba4
+ seems to call hdb_next_enctype2key() in the pre-authentication
+ routines to allow multiple passwords per account in krb5.
+ (I think this was intended to allow multiple salts).
+ AD doesn't support this, so the MIT port shouldn't bother with
+ this.
+
+State Machine safety when using Kerberos and GSSAPI libraries
+-------------------------------------------------------------
+
+Samba's client-side & app-server-side libraries are built on a giant
+state machine, and as such have very different
+requirements to those traditionally expressed for kerberos and GSSAPI
+libraries.
+
+Samba requires all of the libraries it uses to be state machine safe in
+their use of internal data. This does not mean thread safe, and an
+application could be thread safe, but not state machine safe (if it
+instead used thread-local variables).
+
+So, what does it mean for a library to be state machine safe? This is
+mostly a question of context, and how the library manages whatever
+internal state machines it has. If the library uses a context
+variable, passed in by the caller, which contains all the information
+about the current state of the library, then it is safe. An example
+of this state is the sequence number and session keys for an ongoing
+encrypted session).
+
+The other issue affecting state machines is 'blocking' (waiting for a
+read on a network socket). Samba's non-blocking I/O doesn't like
+waiting for libkrb5 to go away for awhile to talk to the KDC.
+
+Samba4 provides a hook 'send_to_kdc', that allows Samba4 to take over the
+IO handling, and run other events in the meantime. This uses a
+'nested event context' (which presents the challenges that the kerberos
+library might be called again, while still in the send_to_kdc hook).
+
+Heimdal has this 'state machine safety' in parts, and we have modified
+the lorikeet branch to improve this behaviour, when using a new,
+non-standard API to tunnelling a ccache (containing a set of tickets)
+through the gssapi, by temporarily casting the ccache pointer to a
+gss credential pointer.
+This new API is Heimdal's samba4-requested gss_krb5_import_cred() fcn;
+this will have to be rewritten or ported in the MIT port.
+
+This replaces an older scheme using the KRB5_CCACHE
+environment variable to get the same job done. This tunnelling trick
+enables a command-line app-client to run kinit tacitly, before running
+GSSAPI for service-authentication. This tunnelling trick avoids the
+more usual approach of keeping the ccache pointer in a global variable.
+
+No longer true; the krb5_context global is gone now:
+[Heimdal uses a per-context variable for the 'krb5_auth_context', which
+controls the ongoing encrypted connection, but does use global
+variables for the ubiquitous krb5_context parameter.]
+
+The modification that has added most to 'state machine safety' of
+GSSAPI is the addition of the gss_krb5_acquire_creds() function. This
+allows the caller to specify a keytab and ccache, for use by the
+GSSAPI code. Therefore there is no need to use global variables to
+communicate this information about keytab & ccache.
+
+At a more theoretical level (simply counting static and global
+variables) Heimdal is not state machine safe for the GSSAPI layer.
+(Heimdal is now (6/09) much more nearly free of globals.)
+The Krb5 layer alone is much closer, as far as I can tell, blocking
+excepted. .
+
+
+As an alternate to fixing MIT Kerberos for better safety in this area,
+a new design might be implemented in Samba, where blocking read/write
+is made to the KDC in another (fork()ed) child process, and the results
+passed back to the parent process for use in other non-blocking operations.
+
+To deal with blocking, we could have a fork()ed child per context,
+using the 'GSSAPI export context' function to transfer
+the GSSAPI state back into the main code for the wrap()/unwrap() part
+of the operation. This will still hit issues of static storage (one
+gss_krb5_context per process, and multiple GSSAPI encrypted sessions
+at a time) but these may not matter in practice.
+
+This approach has long been controversial in the Samba team.
+An alternate way would be to be implement E_AGAIN in libkrb5: similar
+to the way to way read() works with incomplete operations. to do this
+in libkrb5 would be difficult, but valuable.
+
+In the short-term, we deal with blocking by taking over the network
+send() and recv() functions, therefore making them 'semi-async'. This
+doesn't apply to DNS yet.These thread-safety context-variables will
+probably present porting problems, during the MIT port. This will
+probably be most of the work in the port to MIT.
+
+
+
+GSSAPI and Kerberos extensions
+------------------------------
+
+This is a general list of the other extensions we have made to / need from
+the kerberos libraries
+
+ - DCE_STYLE : Microsoft's hard-coded 3-msg Challenge/Response handshake
+ emulates DCE's preference for C/R. Microsoft calls this DCE_STYLE.
+ MIT already has this nowadays (6/09).
+
+ - gsskrb5_get_initiator_subkey() (return the exact key that Samba3
+ has always asked for. gsskrb5_get_subkey() might do what we need
+ anyway). This is necessary, because in some spots, Microsoft uses
+ raw Kerberos keys, outside the Kerberos protocols, and not using Kerberos
+ wrappings etc. Ie, as a direct input to MD5 and ARCFOUR, without using
+ the make_priv() or make_safe() calls.
+
+ - gsskrb5_acquire_creds() (takes keytab and/or ccache as input
+ parameters, see keytab and state machine discussion in prev section)
+
+Not needed anymore, because MIT's code now handles PACs fully:
+ - gss_krb5_copy_service_keyblock() (get the key used to actually
+ encrypt the ticket to the server, because the same key is used for
+ the PAC validation).
+ - gsskrb5_extract_authtime_from_sec_context (get authtime from
+ kerberos ticket)
+ - gsskrb5_extract_authz_data_from_sec_context (get authdata from
+ ticket, ie the PAC. Must unwrap the data if in an AD-IFRELEVENT)]
+The new function to handle the PAC fully
+ - gsskrb5_extract_authz_data_from_sec_context()
+
+Samba still needs this one:
+ - gsskrb5_wrap_size (find out how big the wrapped packet will be,
+ given input length).
+
+Keytab requirements
+-------------------
+
+Because windows machine account handling is very different to the
+traditional 'MIT' keytab operation.
+This starts when we look at the basics of the secrets handling:
+
+Samba file-servers can have many server-name simultaneously (kindof
+like web servers' software virtual hosting), but since these servers
+are running in AD, these names are free to be set up to all share
+the same secret key. In AD, host-sharing server names almost always
+share a secret key like this. In samba3, this key-sharing was optional, so
+some samba3 hosts' keytabs did hold multiple keys. samba4 abandons this
+traditional "old MIT" style of keytab, and only supports one key per keytab,
+and multiple server-names can use that keytab key in common.
+Heimdal offered "in-memory keytabs" for servers that use passwords.
+These server-side passwords were held in a Samba LDB database called secrets.ldb,
+and the heimdal library would be supplied the password from the ldb file and
+would construct an in-memory keytab struct containing the password,
+just as if the library had read an MIT-style keytab file.
+Unfortunately, only later, at recv_auth() time, would the heimdal library
+convert the PW into a salted-&-hashed AES key, by hashing 10,000 times with
+SHA-1. So, nowadays, this password-based in-memory keytab is seen as too
+slow, and is falling into disuse.
+
+Traditional 'MIT' behaviour is to use a keytab, containing salted key
+data, extracted from the KDC. (In this model, there is no 'service
+password', instead the keys are often simply application of random
+bytes). Heimdal also implements this behaviour.
+
+The windows model is very different - instead of sharing a keytab with
+each member server, a random utf-16 pseudo-textual password is stored
+for the whole machine.
+The password is set with non-kerberos mechanisms (particularly SAMR,
+a DCE-RPC service) and when interacting on a kerberos basis, the
+password is salted by the member server (ie, an AD server-host).
+(That is, no salt information appears to be conveyed from the AD KDC
+to the member server. ie, the member server must use the rule's
+described in Luke's mail above).
+
+pre-win7 AD and samba3/4 both use SAMR, an older protocol, to jumpstart
+the member server's PW-sharing with AD (the "windows domain-join process").
+This PW-sharing transfers only the PW's utf-16 text, without any salting
+or hashing, so that non-krb security mechanisms can use the same utf-16
+text PW. for windows 7, this domain-joining uses LDAP for PW-setting.
+
+In dealing with this model, we use both the traditional file
+keytab and in-MEMORY keytabs.
+
+When dealing with a windows KDC, the behaviour regarding case
+sensitivity and canonacolisation must be accomidated. This means that
+an incoming request to a member server may have a wide variety of
+service principal names. These include:
+
+machine$@REALM (samba clients)
+HOST/foo.bar@realm (win2k clients)
+HOST/foo@realm (win2k clients, using netbios)
+cifs/foo.bar@realm (winxp clients)
+cifs/foo@realm (winxp clients, using netbios)
+
+as well as all case variations on the above.
+
+Heimdal's GSSAPI expects to get a principal-name & a keytab, possibly containing
+multiple principals' different keys. However, AD has a different problem to
+solve, which is that the client may know the member-server by a non-canonicalized
+principal name, yet AD knows the keytab contains exactly one key, indexed by
+the canonical name. So, GSSAPI is unprepared to canonicalize the server-name
+that the cliet requested, and is also overprepared to do an unnecessary search
+through the keytab by principal-name. So samba's server-side GSSAPI calls game
+the GSSAPI, by supplying the server's known canonical name, and the one-key keytab.
+this doesn't really affect the port to mit-krb.
+
+Because the number of U/L case combinations got 'too hard' to put into a keytab in the
+traditional way (with the client to specify the name), we either
+pre-compute the keys into a traditional keytab or make an in-MEMORY
+keytab at run time. In both cases we specify the principal name to
+GSSAPI, which avoids the need to store duplicate principals.
+
+We use a 'private' keytab in our private dir, referenced from the
+secrets.ldb by default.
+
+Extra Heimdal functions used
+----------------------------
+these fcns didn't exist in the MIT code, years ago, when samba started.
+AB will try to build a final list of these fcns.
+
+(an attempt to list some of the Heimdal-specific functions I know we use)
+
+krb5_free_keyblock_contents()
+
+also a raft of prinicpal manipulation functions:
+
+Prncipal Manipulation
+---------------------
+
+Samba makes extensive use of the principal manipulation functions in
+Heimdal, including the known structure behind krb_principal and
+krb5_realm (a char *). for example,
+krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
+ KRB5_PRINCIPAL_PARSE_MUST_REALM, &principal);
+krb5_princ_realm(smb_krb5_context->krb5_context, principal);
+krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM, &new_princ);
+These are needed for juggling the AD variant-structures for server names.
+
+Authz data extraction
+---------------------
+
+We use krb5_ticket_get_authorization_data_type(), and expect it to
+return the correct authz data, even if wrapped in an AD-IFRELEVENT container.
+
+KDC/hdb Extensions
+--------------
+
+We have modified Heimdal's 'hdb' interface to specify the 'class' of
+Principal being requested. This allows us to correctly behave with
+the different 'classes' of Principal name. This is necessary because
+of the AD structure, which uses very different record-structures
+for user-principals, trust principals & server-principals.
+
+We currently define 3 classes:
+ - client (kinit)
+ - server (tgt)
+ - krbtgt (kinit, tgt) the kdc's own ldap record
+
+I also now specify the kerberos principal as an explicit parameter to LDB_fetch(),
+not an in/out value on the struct hdb_entry parameter itself.
+
+Private Data pointer (and windc hooks) (see above):
+ In addition, I have added a new interface hdb_fetch_ex(), which
+ returns a structure including a private data-pointer, which may be used
+ by the windc plugin interface functions. The windc plugin provides
+ the hook for the PAC, as well as a function for the main access control routines.
+
+ A new windc plugin function should be added to increment the bad password counter
+ on failure.
+
+libkdc (doesn't matter for IPA; Samba invokes the Heimdal kdc as a library call,
+but this is just a convenience, and the MIT port can do otherwise w/o trouble.)
+------
+
+Samba4 needs to be built as a single binary (design requirement), and
+this should include the KDC. Samba also (and perhaps more
+importantly) needs to control the configuration environment of the
+KDC.
+
+The interface we have defined for libkdc allow for packet injection
+into the post-socket layer, with a defined krb5_context and
+kdb5_kdc_configuration structure. These effectively redirect the
+kerberos warnings, logging and database calls as we require.
+
+Using our socket lib (para 3 does matter for the send_to_kdc() plugin).
+See also the discussion about state machine safety above)
+--------------------
+
+An important detail in the use of libkdc is that we use samba4's own socket
+lib. This allows the KDC code to be as portable as the rest of samba
+(this cuts both ways), but far more importantly it ensures a
+consistency in the handling of requests, binding to sockets etc.
+
+To handle TCP, we use of our socket layer in much the same way as
+we deal with TCP for CIFS. Tridge created a generic packet handling
+layer for this.
+
+For the client, samba4 likewise must take over the socket functions,
+so that our single thread smbd will not lock up talking to itself.
+(We allow processing while waiting for packets in our socket routines).
+send_to_kdc() presents to its caller the samba-style socket interface,
+but the MIT port will reimplement send_to_kdc(), and this routine will
+use internally the same socket library that MIT-krb uses.
+
+Kerberos logging support (this will require porting attention)
+------------------------
+
+Samba4 now (optionally in the main code, required for the KDC) uses the
+krb5_log_facility from Heimdal. This allows us to redirect the
+warnings and status from the KDC (and client/server kerberos code) to
+Samba's DEBUG() system.
+
+Similarly important is the Heimdal-specific krb5_get_error_string()
+function, which does a lot to reduce the 'administrator pain' level,
+by providing specific, english text-string error messages instead of
+just error code translations. (this isn't necessarty for the port,
+but it's more useful than MIT's default err-handling; make sure
+this works for MIT-krb)
+
+
+Short name rules
+----------------
+
+Samba is highly likely to be misconfigured, in many weird and
+interesting ways. As such, we have a patch for Heimdal that avoids
+DNS lookups on names without a . in them. This should avoid some
+delay and root server load. (this may need to be ported to MIT.)
+
+PAC Correctness
+---------------
+
+We now put the PAC into the TGT, not just the service ticket.
+
+Forwarded tickets
+-----------------
+
+We extract forwarded tickets from the GSSAPI layer, and put
+them into the memory-based credentials cache.
+We can then use them for proxy work.
+
+
+Kerberos TODO
+=============
+
+(Feel free to contribute to any of these tasks, or ask
+abartlet@samba.org about them).
+
+Lockout Control (still undone in samba4 on heimdal)
+--------------
+
+We need to get (either if PADL publishes their patch, or write our
+own) access control hooks in the Heimdal KDC. We need to lockout
+accounts (eg, after 10 failed PW-attemps), and perform other controls.
+This is standard AD behavior, that samba4 needs to get right, whether
+heimdal or MIT-krb is doing the ticket work.
+
+Gssmonger
+---------
+
+Microsoft has released a krb-specific testsuite called gssmonger,
+which tests interop. We should compile it against lorikeet-heimdal,
+MIT and see if we can build a 'Samba4' server for it.
+GSSMonger wasn't intended to be Windows-specific.
+
+Kpasswd server (kpasswd server is now finished, but not testsuite)
+--------------
+
+I have a partial kpasswd server which needs finishing, and a we need a
+client testsuite written, either via the krb5 API or directly against
+GENSEC and the ASN.1 routines.
+Samba4 likes to test failure-modes, not just successful behavior.
+
+Currently it only works for Heimdal, not MIT clients. This may be due
+to call ordering constraints.
+
+
+Correct TCP support
+-------------------
+
+Samba4 socket-library's current TCP support does not send back 'too large'
+error messages if the high bit is set. This is needed for a proposed extension
+mechanism (SSL-armored kinit, by Leif Johansson <leifj@it.su.se>),
+but is likewise unsupported in both current Heimdal and MIT.
+
+=========================================================================
+AB says MIT's 1.7 announcement about AD support covers Luke Howard's
+changes. It all should be easy for IPA to exploit/use during the port
+of Samba4 to MIT.
+AB says Likewise software will likely give us their freeware NTLM/MIT-krb
+implementation.
diff --git a/source4/auth/kerberos/kerberos-porting-to-mit-notes.txt b/source4/auth/kerberos/kerberos-porting-to-mit-notes.txt
new file mode 100644
index 0000000..9df3a13
--- /dev/null
+++ b/source4/auth/kerberos/kerberos-porting-to-mit-notes.txt
@@ -0,0 +1,803 @@
+Copyright Andrew Bartlett <abartlet@samba.org> 2005-2009
+Copyright Donald T. Davis <don@mit.edu> 2009
+
+Released under the GPLv3
+"Porting Samba4 to MIT-Krb"
+
+
+ From Idmwiki
+
+
+IPA v3 will use a version of Samba4 built on top of MIT's Kerberos
+implementation, instead of Heimdal's version of Kerberos.
+
+Task list summary for porting changes needed, from Andrew Bartlett:
+
+ * Rewrite or extend the LDAP driver that MIT-KDC will use.
+ * MIT KDC changes: rewrite DAL, add TGS-KBAC, enable PACs,...
+ * Full thread-safety for MIT's library code,
+ * Many small changes
+
+Task list, without explanations (the list with explanations is in the
+later sections of this document):
+
+Porting Samba4 to MIT-krb comprises four main chunks of work:
+ 1. Rewrite or extend the LDAP driver that MIT-KDC will use:
+ a. Our LDAP driver for the KDB needs to know how to do
+ Samba4's intricate canonicalization of server names,
+ user-names, and realm names.
+ b. AD-style aliases for HOST/ service names.
+ c. Implicit names for Win2k accounts.
+ d. Principal "types": client / server / krbtgs
+ e. Most or all of this code is in 3 source files,
+ ~1000 lines in all;
+ 2. MIT KDC changes
+ a. Rewrite the MIT KDC's Data-Abstraction Layer (DAL),
+ mostly because he MIT KDC needs to see& manipulate
+ more LDAP detail, on Samba4's behalf;
+ b. Add HBAC to the KDC's TGT-issuance, so that Samba4
+ can refuse TGTs to kinit, based on time-of-day&
+ IP-addr constraints;
+ c. turn on MIT-krb 1.7's PAC handling
+ d. add bad-password counts, for unified account-lockouts
+ across all authT methods (Krb, NTLM, LDAP simple bind,
+ etc)
+ 3. Make sure MIT's library code is more fully thread-safe,
+ by replacing all global and static variables with context
+ parameters for the library routines. This may already be
+ done.
+ 4. Many small changes (~15)
+ a. some extensions to MIT's libkrb5& GSSAPI libraries,
+ including GSSAPI ticket-forwarding
+ b. some refitting in Samba4's use of the MIT libraries;
+ c. make sure Samba4's portable socket API works,
+ including "packet too large" errors;
+ d. MIT's GSSAPI code should support some legacy Samba3
+ clients that present incorrectly-calculated checksums;
+ e. Samba4 app-server-host holds aUTF-16 PW, plus a
+ key bitstring;
+ f. in-memory-only credentials cache;
+ g. in-memory-only keytab (nice to have);
+ h. get OSS NTLM authT library (Likewise Software?);
+ i. special Heimdal-specific functions;
+ j. principal-manipulation functions;
+ k. special check for misconfigured Samba4 hostnames;
+ l. improved krb error-messages;
+ m. improved krb logging
+ n. MS GSSMonger test-suite
+ o. testsuite for kpasswd daemon
+
+0. Introduction: This document should be read alongside the Samba4
+source code, as follows:
+
+ * For DAL and KDC requirements, please see Samba4's
+ source4/kdc/hdb-samba4.c in particular. This file
+ is an implementation against Heimdal's HDB abstraction
+ layer, and is the biggest part of the samba-to-krb
+ glue layer, so the main part of the port to MIT is
+ to replace hdb-samba4 with a similar glue layer
+ that's designed for MIT's code.
+ * Samba4's PAC requirements are implemented in
+ source4/kdc/pac-glue.c
+ * Both of the above two layers are Heimdal plugins, and
+ both get loaded in source4/kdc/kdc.c
+ * For GSSAPI requirements, see auth/gensec/gensec_gssapi.c
+ (the consumer of GSSAPI in Samba4)
+ * For Kerberos library requirements, see
+ auth/kerberos/krb5_init_context.c
+ * Samba has its own credentials system, wrapping GSS creds,
+ just as GSS creds wrap around krb5 creds. For the
+ interaction between Samba4 credential system and GSSAPI
+ and Kerberos, see auth/credentials/credentials_krb5.
+
+1. Rewrite or extend the LDAP driver that MIT-KDC will use.
+
+ a. IPA'sLDAP driver for the KDB needs to know how to do
+ Samba4's intricate canonicalization of server names,
+ user-names, and realm names.
+ For hostnames& usernames, alternate names appear in
+ LDAP as extra values in the multivalued "principal name"
+ attributes:
+ * For a hostname, the alternate names (other than
+ the short name, implied from the CN), are stored in
+ the servicePrincipalName
+ * For a username, the alternate names are stored in
+ the userPrincipalName attribute, and can be long
+ email-address-like names, such as joe@microsoft.com
+ (see "Type 10 names," below).
+ GSSAPI layer requirements: Welcome to the wonderful
+ world of canonicalisation. The MIT Krb5 libs (including
+ GSSAPI) do not enable the AS to send kinit a TGT containing
+ a different realm-name than what the client asked for,
+ even in U/L case differences. Heimdal has the same problem,
+ and this applies to the krb5 layer too, not just GSSAPI.
+ There are two kinds of name-canonicalization that can
+ occur on Windows:
+ * Lower-to-upper case conversion, because Windows domain
+ names are usually in upper case;
+ * An unrecognizable substitution of names, such as might
+ happen when a user requests a ticket for a NetBIOS domain
+ name, but gets back a ticket for the corresponding FQDN.
+ As developers, we should test if the AD KDC's name-canonical-
+ isation can be turned off with the KDCOption flags in the
+ AS-REQ or TGS-REQ; Windows clients always send the
+ Canonicalize flags as KDCOption values.
+ Principal Names, long and short names:
+ AD's KDC does not canonicalize servicePrincipalNames, except
+ for the realm in the KDC reply. That is, the client gets
+ back the principal it asked for, with the realm portion
+ 'fixed' to uppercase, long form.
+ Samba4 does some canonicalization, though Heimdal doesn't
+ canonicalize names itself: For hostnames and usernames,
+ Samba4 canonicalizes the requested name only for the LDAP
+ principal-lookup, but then Samba4 returns the retrieved LDAP
+ record with the request's original, uncanonicalized hostname
+ replacing the canonicalized name that actually was found.
+ Usernames: AndrewB says that Samba4 used to return
+ the canonicalized username exactly as retrieved from LDAP.
+ The reason Samba4 treated usernames differently was that
+ the user needs to present his own canonicalized username
+ to servers, for ACL-matching. For hostnames this isn't
+ necessary.
+ Realm-names: AD seems to accept a realm's short name
+ in krb-requests, at least for AS_REQ operations, but the
+ AD KDC always performs realm-canonicalisation, which
+ converts the short realm-name to the canonical long form.
+ So, this causes pain for current krb client libraries.
+ Punchline: For bug-compatibility, we may need to
+ selectively or optionally disable the MIT-KDC's name-
+ canonicalization.
+ Application-code:
+ Name-canonicalisation matters not only for the KDC, but
+ also for app-server-code that has to deal with keytabs.
+ Further, with credential-caches, canonicalization can
+ lead to cache-misses, but then the client just asks for
+ new credentials for the variant server-name. This could
+ happen, for example, if the user asks to access the
+ server twice, using different variants of the server-name.
+ Doubled realm-names: We also need to handle type 10
+ names (NT-ENTERPRISE), which are a full principal name
+ in the principal field, unrelated to the realm. The
+ principal field contains both principal& realm names,
+ while the realm field contains a realm name, too, possibly
+ different. For example, an NT-ENTERPRISE principal name
+ might look like: joeblow@microsoft.com@NTDEV.MICROSOFT.COM ,
+ <--principal field-->|<----realm name--->|
+ Where joe@microsoft.com is the leading portion, and
+ NTDEV.MICROSOFT.COM is the realm. This is used for the
+ 'email address-like login-name' feature of AD.
+ b.AD-style aliases for HOST/ service names.
+ AD keeps a list of service-prefixed aliases for the host's
+ principal name. The AD KDC reads& parses this list, so
+ as to allow the aliased services to share the HOST/ key.
+ This means that every ticket-request for a service-alias
+ gets a service-ticket encrypted in the HOST/ key.
+ For example, this is how HTTP/ and CIFS/ can use the
+ HOST/ AD-LDAP entry, without any explicitly CIFS-prefixed
+ entry in the host's servicePrincipalName attribute. In the
+ app-server host's AD record, the servicePrincipalName says
+ only HOST/my.computer@MY.REALM , but the client asks
+ for CIFS/my.omputer@MY.REALM tickets. So, AD looks in
+ LDAP for both name-variants, and finds the HOST/ version,
+ In AD's reply, AD replaces the HOST/ prefix with CIFS/ .
+ We implement this in hdb-ldb.
+ (TBD: Andrew, is this correct?:)
+ List of HOST/ aliases: Samba4 currently uses only a small
+ set of HOST/ aliases: sPNMappings: host=ldap,dns,cifs,http .
+ Also, dns's presence in this list is a bug, somehow.
+ AD's real list has 53 entries:
+ sPNMappings: host=alerter,appmgmt,cisvc,clipsrv,browser,
+ dhcp,dnscache,replicator,eventlog,eventsystem,policyagent,
+ oakley,dmserver,dns,mcsvc,fax,msiserver,ias,messenger,
+ netlogon,netman,netdde,netddedsm,nmagent,plugplay,
+ protectedstorage,rasman,rpclocator,rpc,rpcss,remoteaccess,
+ rsvp,samss,scardsvr,scesrv,seclogon,scm,dcom,cifs,spooler,
+ snmp,schedule,tapisrv,trksvr,trkwks,ups,time,wins,www,
+ http,w3svc,iisadmin,msdtc
+ Domain members that expect the longer list will break in
+ Samba4, as of 6/09. AB says he'll try to fix this right
+ away. There is another post somewhere (ref lost for the
+ moment) that details where in active directory the long
+ list of stored aliases for HOST/ is.
+ c.Implicit names for Win2000 Accounts: AD keys its
+ server-records by CN or by servicePrincipalName, but a
+ win2k box's server-entry in LDAP doesn't include the
+ servicePrincipalName attribute, So, win2k server-accounts
+ are keyed by the CN attribute instead. Because AD's LDAP
+ doesn't have a servicePrincipalName for win2k servers'
+ entries, Samba4 has to have an implicit mapping from
+ host/computer.full.name and from host/computer, to the
+ computer's CN-keyed entry in the AD LDAP database, so to
+ be able to find the win2k server's host name in the KDB.
+ d.Principal "types":
+ We have modified Heimdal's 'hdb' interface to specify
+ the 'class' of Principal being requested. This allows
+ us to correctly behave with the different 'classes' of
+ Principal name. This is necessary because of AD's LDAP
+ structure, which uses very different record-structures
+ for user-principals, trust principals& server-principals.
+ We currently define 3 classes:
+ * client (kinit)
+ * server (tgt)
+ * krbtgt the TGS's own ldap record
+ Samba4 also now specifies the kerberos principal as an
+ explicit parameter to LDB_fetch(), not an in/out value
+ on the struct hdb_entry parameter itself.
+ e. Most or all of this LDAP driver code is in three source
+ files, ~1000 lines in all. These files are in
+ samba4/kdc :
+ * hdb-samba4.c (samba4-to-kdb glue-layer plugin)
+ * pac-glue.c (samba4's pac glue-layer plugin)
+ * kdc.c (loads the above two plugins).
+
+2. MIT KDC changes
+
+ a.Data-Abstraction Layer (DAL): It would be good to
+ rewrite or circumvent the MIT KDC's DAL, mostly because
+ the MIT KDC needs to see& manipulate more LDAP detail,
+ on Samba4's behalf. AB says the MIT DAL may serve well-
+ enough, though, mostly as is. AB says Samba4 will need
+ the private pointer part of the KDC plugin API, though,
+ or the PAC generation won't work (see sec.2.c, below).
+ * MIT's DAL calls lack context parameters (as of 2006),
+ so presumably they rely instead on global storage, and
+ aren't fully thread-safe.
+ * In Novell's pure DAL approach, the DAL only read in the
+ principalName as the key, so it had trouble performing
+ access-control decisions on things other than the user's
+ name (like the addresses).
+ * Here's why Samba4 needs more entry detail than the DAL
+ provides: The AS needs to have ACL rules that will allow
+ a TGT to a user only when the user logs in from the
+ right desktop addresses, and at the right times of day.
+ This coarse-granularity access-control could be enforced
+ directly by the KDC's LDAP driver, without Samba having
+ to see the entry's pertinent authZ attributes. But,
+ there's a notable exception: a user whose TGT has
+ expired, and who wants to change his password, should
+ be allowed a restricted-use TGT that gives him access
+ to the kpasswd service. This ACL-logic could be buried
+ in the LDAP driver, in the same way as the TGS ACL could
+ be enforced down there, but to do so would just be even
+ uglier than it was to put the TGS's ACL-logic in the driver.
+ * Yet another complaint is that the DAL always pulls an
+ entire LDAP entry, non-selectively. The current DAL
+ is OK for Samba4's purposes, because Samba4 only reads,
+ and doesn't write, the KDB. But this all-or-nothing
+ retrieval hurts the KDC's performance, and would do so
+ even more, if Samba had to use the DAL to change KDB
+ entries.
+ b.Add HBAC to the KDC's TGT-issuance, so that Samba4 can
+ refuse TGTs to kinit, based on time-of-day& IP-address
+ constraints. AB asks, "Is a DAL the layer we need?"
+ Looking at what we need to pass around, AB doesn't think
+ the DAL is the right layer; what we really want instead
+ is to create an account-authorization abstraction layer
+ (e.g., is this account permitted to login to this computer,
+ at this time?). Samba4 ended up doing account-authorization
+ inside Heimdal, via a specialized KDC plugin. For a summary
+ description of this plugin API, see Appendix 2.
+ c. Turn on MIT-krb 1.7'sPAC handling.
+ In addition, I have added a new interface hdb_fetch_ex(),
+ which returns a structure including a private data-pointer,
+ which may be used by the windc plugin interface functions.
+ The windc plugin provides the hook for the PAC.
+ d. Samba4 needsaccess control hooks in the Heimdal& MIT
+ KDCs. We need to lockout accounts (eg, after 10 failed PW-
+ attempts), and perform other controls. This is standard
+ AD behavior, that Samba4 needs to get right, whether
+ Heimdal or MIT-krb is doing the ticket work.
+ - If PADL doesn't publish their patch for this,
+ we'll need to write our own.
+ - The windc plugin proivides a function for the main
+ access control routines. A new windc plugin function
+ should be added to increment the bad password counter
+ on failure.
+ - Samba4 doesn't yet handle bad password counts (or good
+ password notification), so that a single policy can be
+ applied against all means of checking a password (NTLM,
+ Kerberos, LDAP Simple Bind, etc). Novell's original DAL
+ did not provide a way to update the PW counts information.
+ - Nevertheless, we know that this is very much required in
+ AD, because Samba3 + eDirectory goes to great lengths to
+ update this information. This may have been addressed in
+ Simo's subsequent IPA-KDC design),
+ * AllowedWorkstationNames and Krb5: Microsoft uses the
+ clientAddresses *multiple value* field in the krb5
+ protocol (particularly the AS_REQ) to communicate the
+ client's netbios name (legacy undotted name,<14 chars)
+ AB guesses that this is to support the userWorkstations
+ field (in user's AD record). The idea is to support
+ client-address restrictions, as was standard in NT:
+ The AD authentication server probably checks the netbios
+ address against this userWorkstations value (BTW, the
+ NetLogon server does this, too).
+
+3. State Machine safety
+when using Kerberos and GSSAPI libraries
+
+ * Samba's client-side& app-server-side libraries are built
+ on a giant state machine, and as such have very different
+ requirements to those traditionally expressed for kerberos
+ and GSSAPI libraries.
+ * Samba requires all of the libraries it uses to be "state
+ machine safe" in their use of internal data. This does not
+ necessarily mean "thread safe," and an application could be
+ thread safe, but not state machine safe (if it instead used
+ thread-local variables). so, if MIT's libraries were made
+ thread-safe only by inserting spinlock() code, then the MIT
+ libraries aren't yet "state machine safe."
+ * So, what does it mean for a library to be state machine safe?
+ This is mostly a question of context, and how the library manages
+ whatever internal state machines it has. If the library uses a
+ context variable, passed in by the caller, which contains all
+ the information about the current state of the library, then it
+ is safe. An example of this state is the sequence number and
+ session keys for an ongoing encrypted session).
+ * The other issue affecting state machines is 'blocking' (waiting for a
+ read on a network socket). Samba's non-blocking I/O doesn't like
+ waiting for libkrb5 to go away for awhile to talk to the KDC.
+ * Samba4 provides a hook 'send_to_kdc', that allows Samba4 to take over the
+ IO handling, and run other events in the meantime. This uses a
+ 'nested event context' (which presents the challenges that the kerberos
+ library might be called again, while still in the send_to_kdc hook).
+ * Heimdal has this 'state machine safety' in parts, and we have modified
+ Samba4's lorikeet branch to improve this behaviour, when using a new,
+ non-standard API to tunnelling a ccache (containing a set of tickets)
+ through the gssapi, by temporarily casting the ccache pointer to a
+ gss credential pointer. This new API is Heimdal's samba4-requested
+ gss_krb5_import_cred() fcn; this will have to be rewritten or ported
+ in the MIT port.
+ * This tunnelling trick replaces an older scheme using the KRB5_CCACHE
+ environment variable to get the same job done. The tunnelling trick
+ enables a command-line app-client to run kinit tacitly, before running
+ GSSAPI for service-authentication. The tunnelling trick avoids the
+ more usual approach of keeping the ccache pointer in a global variable.
+ * [Heimdal uses a per-context variable for the 'krb5_auth_context',
+ which controls the ongoing encrypted connection, but does use global
+ variables for the ubiquitous krb5_context parameter. (No longer true,
+ because the krb5_context global is gone now.)]
+ * The modification that has added most to 'state machine safety' of
+ GSSAPI is the addition of the gss_krb5_acquire_creds() function.
+ This allows the caller to specify a keytab and ccache, for use by
+ the GSSAPI code. Therefore there is no need to use global variables
+ to communicate this information about keytab& ccache.
+ * At a more theoretical level (simply counting static and global
+ variables) Heimdal is not state machine safe for the GSSAPI layer.
+ (But Heimdal is now (6/09) much more nearly free of globals.)
+ The Krb5 layer alone is much closer, as far as I can tell, blocking
+ excepted. .
+ * As an alternate to fixing MIT Kerberos for better safety in this area,
+ a new design might be implemented in Samba, where blocking read/write
+ is made to the KDC in another (fork()ed) child process, and the results
+ passed back to the parent process for use in other non-blocking operations.
+ * To deal with blocking, we could have a fork()ed child per context,
+ using the 'GSSAPI export context' function to transfer
+ the GSSAPI state back into the main code for the wrap()/unwrap() part
+ of the operation. This will still hit issues of static storage (one
+ gss_krb5_context per process, and multiple GSSAPI encrypted sessions
+ at a time) but these may not matter in practice.
+ * This approach has long been controversial in the Samba team.
+ An alternate way would be to be implement E_AGAIN in libkrb5: similar
+ to the way to way read() works with incomplete operations. to do this
+ in libkrb5 would be difficult, but valuable.
+ * In the short-term, we deal with blocking by taking over the network
+ send() and recv() functions, therefore making them 'semi-async'. This
+ doesn't apply to DNS yet.These thread-safety context-variables will
+ probably present porting problems, during the MIT port. This will
+ probably be most of the work in the port to MIT.
+ This may require more thorough thread-safe-ing work on the MIT libraries.
+
+4. Many small changes (~15)
+
+ a. Some extensions to MIT'slibkrb5& GSSAPI libraries, including
+ GSSAPI ticket-forwarding: This is a general list of the other
+ extensions Samba4 has made to / need from the kerberos libraries
+ * DCE_STYLE : Microsoft's hard-coded 3-msg Challenge/Response handshake
+ emulates DCE's preference for C/R. Microsoft calls this DCE_STYLE.
+ MIT already has this nowadays (6/09).
+ * gsskrb5_get_initiator_subkey() (return the exact key that Samba3
+ has always asked for. gsskrb5_get_subkey() might do what we need
+ anyway). This routine is necessary, because in some spots,
+ Microsoft uses raw Kerberos keys, outside the Kerberos protocols,
+ as a direct input to MD5 and ARCFOUR, without using the make_priv()
+ or make_safe() calls, and without GSSAPI wrappings etc.
+ * gsskrb5_acquire_creds() (takes keytab and/or ccache as input
+ parameters, see keytab and state machine discussion in prev section)
+ * The new function to handle the PAC fully
+ gsskrb5_extract_authz_data_from_sec_context()
+ need to test that MIT's PAC-handling code checks the PAC's signature.
+ * gsskrb5_wrap_size (Samba still needs this one, for finding out how
+ big the wrapped packet will be, given input length).
+ b. Some refitting in Samba4's use of the MIT libraries;
+ c. Make sure Samba4'sportable socket API works:
+ * An important detail in the use of libkdc is that we use samba4's
+ own socket lib. This allows the KDC code to be as portable as
+ the rest of samba, but more importantly it ensures consistency
+ in the handling of requests, binding to sockets etc.
+ * To handle TCP, we use of our socket layer in much the same way as
+ we deal with TCP for CIFS. Tridge created a generic packet handling
+ layer for this.
+ * For the client, samba4 likewise must take over the socket functions,
+ so that our single thread smbd will not lock up talking to itself.
+ (We allow processing while waiting for packets in our socket routines).
+ send_to_kdc() presents to its caller the samba-style socket interface,
+ but the MIT port will reimplement send_to_kdc(), and this routine will
+ use internally the same socket library that MIT-krb uses.
+ * The interface we have defined for libkdc allows for packet injection
+ into the post-socket layer, with a defined krb5_context and
+ kdb5_kdc_configuration structure. These effectively redirect the
+ kerberos warnings, logging and database calls as we require.
+ * Samba4 socket-library's current TCP support does not send back
+ 'too large' error messages if the high bit is set. This is
+ needed for a proposed extension mechanism (SSL-armored kinit,
+ by Leif Johansson<leifj@it.su.se>), but is currently unsupported
+ in both Heimdal and MIT.
+ d. MIT's GSSAPI code should support some legacy Samba3
+ clients that presentincorrectly-calculated checksums.
+ * Old Clients (samba3 and HPUX clients) use 'selfmade'
+ gssapi/krb5 tokens for use in the CIFS session setup.
+ These hand-crafted ASN.1 packets don't follow rfc1964
+ (GSSAPI) perfectly, so server-side krblib code has to
+ be flexible enough to accept these bent tokens.
+ * It turns out that Windows' GSSAPI server-side code is
+ sloppy about checking some GSSAPI tokens' checksums.
+ During initial work to implement an AD client, it was
+ easier to make an acceptable solution (acceptable to
+ Windows servers) than to correctly implement the
+ GSSAPI specification, particularly on top of the
+ (inflexible) MIT Kerberos API. It did not seem
+ possible to write a correct, separate GSSAPI
+ implementation on top of MIT Kerberos's public
+ krb5lib API, and at the time, the effort did not
+ need to extend beyond what Windows would require.
+ * The upshot is that old Samba3 clients send GSSAPI
+ tokens bearing incorrect checksums, which AD's
+ GSSAPI library cheerfully accepts (but accepts
+ the good checksums, too). Similarly, Samba4's
+ Heimdal krb5lib accepts these incorrect checksums.
+ Accordingly, if MIT's krb5lib wants to interoperate
+ with the old Samba3 clients, then MIT's library will
+ have to do the same.
+ * Because these old clients use krb5_mk_req()
+ the app-servers get a chksum field depending on the
+ encryption type, but that's wrong for GSSAPI (see
+ rfc 1964 section 1.1.1). The Checksum type 8003
+ should be used in the Authenticator of the AP-REQ!
+ That (correct use of the 8003 type) would allow
+ the channel bindings, the GCC_C_* req_flags and
+ optional delegation tickets to be passed from the
+ client to the server. However windows doesn't seem
+ to care whether the checksum is of the wrong type,
+ and for CIFS SessionSetups, it seems that the
+ req_flags are just set to 0. This deviant checksum
+ can't work for LDAP connections with sign or seal,
+ or for any DCERPC connection, because those
+ connections do not require the negotiation of
+ GSS-Wrap paraemters (signing or sealing of whole
+ payloads). Note: CIFS has an independent SMB
+ signing mechanism, using the Kerberos key.
+ * For the code that handles the incorrect& correct
+ checksums, see heimdal/lib/gssapi/krb5/accept_sec_context.c,
+ lines 390-450 or so.
+ * This bug-compatibility is likely to be controversial
+ in the kerberos community, but a similar need for bug-
+ compatibility arose around MIT's& Heimdal's both
+ failing to support TGS_SUBKEYs correctly, and there
+ are numerous other cases.
+ seehttps://lists.anl.gov/pipermail/ietf-krb-wg/2009-May/007630.html
+ * So, MIT's krb5lib needs to also support old clients!
+ e. Samba4 app-server-host holds aUTF-16 PW, plus a key bitstring;
+ See Appendix 1, "Keytab Requirements."
+ f.In-memory-only credentials cache for forwarded tickets
+ Samba4 extracts forwarded tickets from the GSSAPI layer,
+ and puts them into the memory-based credentials cache.
+ We can then use them for proxy work. This needs to be
+ ported, if the MIT library doesn't do it yet.
+ g.In-memory-only keytab (nice to have):
+ Heimdal used to offer "in-memory keytabs" for servers that use
+ passwords. These server-side passwords were held in a Samba LDB
+ database called secrets.ldb . The heimdal library would fetch
+ the server's password from the ldb file and would construct an
+ in-memory keytab struct containing the password, somewhat as if
+ the library had read an MIT-style keytab file. Unfortunately,
+ only later, at recv_auth() time, would the Heimdal library convert
+ the server-PW into a salted-&-hashed AES key, by hashing 10,000
+ times with SHA-1. Naturally, this is really too slow for recv_auth(),
+ which runs when an app-server authenticates a client's app-service-
+ request. So, nowadays, this password-based in-memory keytab is
+ falling into disuse.
+ h. Get OSSNTLM authT library: AB says Likewise software
+ probably will give us their freeware "NTLM for MIT-krb"
+ implementation.
+ i. Special Heimdal-specific functions; These functions didn't
+ exist in the MIT code, years ago, when Samba started. AB
+ will try to build a final list of these functions:
+ * krb5_free_keyblock_contents()
+ *
+ j.Principal-manipulation functions: Samba makes extensive
+ use of the principal manipulation functions in Heimdal,
+ including the known structure behind krb_principal and
+ krb5_realm (a char *). For example,
+ * krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
+ KRB5_PRINCIPAL_PARSE_REQUIRE_REALM,&principal);
+ * krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM,&new_princ);
+ * krb5_principal_get_realm()
+ * krb5_principal_set_realm()
+ These are needed for juggling the AD variant-structures
+ for server names.
+ k. SpecialShort name rules check for misconfigured Samba4
+ hostnames; Samba is highly likely to be misconfigured, in
+ many weird and interesting ways. So, we have a patch for
+ Heimdal that avoids DNS lookups on names without a "." in
+ them. This should avoid some delay and root server load.
+ (This errors need to be caught in MIT's library.)
+ l.Improved krb error-messages;
+ krb5_get_error_string(): This Heimdal-specific function
+ does a lot to reduce the 'administrator pain' level, by
+ providing specific, English text-string error messages
+ instead of just error code translations. (This isn't
+ necessary for the port, but it's more useful than MIT's
+ default err-handling; Make sure this works for MIT-krb)
+ m.Improved Kerberos logging support:
+ krb5_log_facility(): Samba4 now uses this Heimdal function,
+ which allows us to redirect the warnings and status from
+ the KDC (and client/server Kerberos code) to Samba's DEBUG()
+ system. Samba uses this logging routine optionally in the
+ main code, but it's required for KDC errors.
+ n. MSGSSMonger test-suite: Microsoft has released a krb-specific
+ testsuite called gssmonger, which tests interoperability. We
+ should compile it against lorikeet-heimdal& MIT and see if we
+ can build a 'Samba4' server for it. GSSMonger wasn't intended
+ to be Windows-specific.
+ o.Testsuite for kpasswd daemon: I have a partial kpasswd server
+ which needs finishing, and a Samba4 needs a client testsuite
+ written, either via the krb5 API or directly against GENSEC and
+ the ASN.1 routines. Samba4 likes to test failure-modes, not
+ just successful behavior. Currently Samba4's kpasswd only works
+ for Heimdal, not MIT clients. This may be due to call-ordering
+ constraints.
+
+
+Appendix 1: Keytab Requirements
+
+ Traditional 'MIT' keytab operation is very different from AD's
+ account-handling for application-servers:
+ a. Host PWs vs service-keys:
+ * Traditional 'MIT' behaviour is for the app-server to use a keytab
+ containing several named random-bitstring service-keys, created
+ by the KDC. An MIT-style keytab holds a different service-key
+ for every kerberized application-service that the server offers
+ to clients. Heimdal also implements this behaviour. MIT's model
+ doesn't use AD's UTF-16 'service password', and no salting is
+ necessary for service-keys, because each service-key is random
+ enough to withstand an exhaustive key-search attack.
+ * In the Windows model, the server key's construction is very
+ different: The app-server itself, not the KDC, generates a
+ random UTF-16 pseudo-textual password, and sends this password
+ to the KDC using SAMR, a DCE-RPC "domain-joining" protocol (but
+ for windows 7, see below). Then, the KDC shares this server-
+ password with every application service on the whole machine.
+ * Only when the app-server uses kerberos does the password get
+ salted by the member server (ie, an AD server-host). (That
+ is, no salt information appears to be conveyed from the AD KDC
+ to the member server, and the member server must use the rules
+ described in Luke's mail, in Appendix 3, below). The salted-
+ and-hashed version of the server-host's PW gets stored in the
+ server-host's keytab.
+ * Samba file-servers can have many server-names simultaneously
+ (kind of like web servers' software-virtual-hosting), but since
+ these servers are running in AD, these names can be set up to
+ all share the same secret key. In AD, co-located server names
+ almost always share a secret key like this. In samba3, this
+ key-sharing was optional, so some samba3 hosts' keytabs did
+ hold multiple keys. Samba4 abandons this traditional "old MIT"
+ style of keytab, and only supports one key per keytab, and
+ multiple server-names can use that keytab key in common. In
+ dealing with this model, Samba4 uses both the traditional file
+ keytab and an in-MEMORY keytabs.
+ * Pre-Windows7 AD and samba3/4 both use SAMR, an older protocol,
+ to jumpstart the member server's PW-sharing with AD (the "windows
+ domain-join process"). This PW-sharing transfers only the PW's
+ UTF-16 text, without any salting or hashing, so that non-krb
+ security mechanisms can use the same utf-16 text PW. For
+ Windows 7, this domain-joining uses LDAP for PW-setting.
+ b. Flexible server-naming
+ * The other big difference between AD's keytabs and MIT's is that
+ Windows offers a lot more flexibility about service-principals'
+ names. When the kerberos server-side library receives Windows-style tickets
+ from an app-client, MIT's krb library (or GSSAPI) must accommodate
+ Windows' flexibility about case-sensitivity and canonicalization.
+ This means that an incoming application-request to a member server
+ may use a wide variety of service-principal names. These include:
+ machine$@REALM (samba clients)
+ HOST/foo.bar@realm (win2k clients)
+ cifs/foo.bar@realm (winxp clients)
+ HOST/foo@realm (win2k clients, using netbios)
+ cifs/foo@realm (winxp clients, using netbios),
+ as well as all upper/lower-case variations on the above.
+ c. Keytabs& Name-canonicalization
+ * Heimdal's GSSAPI expects to to be called with a principal-name& a keytab,
+ possibly containing multiple principals' different keys. However, AD has
+ a different problem to solve, which is that the client may know the member-
+ server by a non-canonicalized principal name, yet AD knows the keytab
+ contains exactly one key, indexed by the canonical name. So, GSSAPI is
+ unprepared to canonicalize the server-name that the cliet requested, and
+ is also overprepared to do an unnecessary search through the keytab by
+ principal-name. So Samba's server-side GSSAPI calls have to "game" the
+ GSSAPI, by supplying the server's known canonical name, with the one-key
+ keytab. This doesn't really affect IPA's port of Samba4 to MIT-krb.
+ * Because the number of U/L case combinations got 'too hard' to put into
+ a keytab in the traditional way (with the client to specify the name),
+ we either pre-compute the keys into a traditional keytab or make an
+ in-MEMORY keytab at run time. In both cases we specify the principal
+ name to GSSAPI, which avoids the need to store duplicate principals.
+ * We use a 'private' keytab in our private dir, referenced from the
+ secrets.ldb by default.
+
+Appendix 2: KDC Plugin for Account-Authorization
+
+Here is how Samba4 ended up doing account-authorization in
+Heimdal, via a specialized KDC plugin. This plugin helps
+bridge an important gap: The user's AD record is much richer
+than the Heimdal HDB format allows, so we do AD-specific
+access-control checks in the plugin's AD-specific layer,
+not in the DB-agnostic KDC server:
+ * We created a separate KDC plugin, with this API:
+ typedef struct
+ hdb_entry_ex { void *ctx;
+ hdb_entry entry;
+ void (*free_entry)(krb5_context, struct hdb_entry_ex *);
+ } hdb_entry_ex;
+ The void *ctx is a "private pointer," provided by the
+ 'get' method's hdb_entry_ex retval. The APIs below use
+ the void *ctx so as to find additional information about
+ the user, not contained in the hdb_entry structure.
+ Both the provider and the APIs below understand how to
+ cast the private void *ctx pointer.
+ typedef krb5_error_code
+ (*krb5plugin_windc_pac_generate)(void * krb5_context,
+ struct hdb_entry_ex *,
+ krb5_pac*);
+ typedef krb5_error_code
+ (*krb5plugin_windc_pac_verify)(void * krb5_context,
+ const krb5_principal,
+ struct hdb_entry_ex *,
+ struct hdb_entry_ex *,
+ krb5_pac *);
+ typedef krb5_error_code
+ (*krb5plugin_windc_client_access)(void * krb5_context,
+ struct hdb_entry_ex *,
+ KDC_REQ *,
+ krb5_data *);
+ The krb5_data* here is critical, so that samba's KDC can return
+ the right NTSTATUS code in the 'error string' returned to the
+ client. Otherwise, the windows client won't get the right error
+ message to the user (such as 'password expired' etc). The pure
+ Kerberos error is not enough)
+ typedef struct
+ krb5plugin_windc_ftable { int minor_version;
+ krb5_error_code (*init)(krb5_context, void **);
+ void (*fini)(void *);
+ krb5plugin_windc_pac_generate pac_generate;
+ krb5plugin_windc_pac_verify pac_verify;
+ krb5plugin_windc_client_access client_access;
+ } krb5plugin_windc_ftable;
+ This API has some Heimdal-specific stuff, that'll
+ have to change when we port this KDC plugin to MIT krb.
+ * 1st callback (pac_generate) creates an initial PAC from the user's AD record.
+ * 2nd callback (pac_verify) checks that a PAC is correctly signed,
+ adds additional groups (for cross-realm tickets)
+ and re-signs with the key of the target kerberos
+ service's account
+ * 3rd callback (client_access) performs additional access checks, such as
+ allowedWorkstations and account expiry.
+ * For example, to register this plugin, use the kdc's standard
+ plugin-system at Samba4's initialisation:
+ /* first, setup the table of callback pointers */
+ /* Registar WinDC hooks */
+ ret = krb5_plugin_register(krb5_context, PLUGIN_TYPE_DATA,
+ "windc",&windc_plugin_table);
+ /* once registered, the KDC will invoke the callbacks */
+ /* while preparing each new ticket (TGT or app-tkt) */
+ * An alternative way to register the plugin is with a
+ config-file that names a DSO (Dynamically Shared Object).
+
+Appendix 3: Samba4 stuff that doesn't need to get ported.
+
+Heimdal oddities
+* Heimdal is built such that it should be able to serve multiple realms
+ at the same time. This isn't relevant for Samba's use, but it shows
+ up in a lot of generalisations throughout the code.
+* Samba4's code originally tried internally to make it possible to use
+ Heimdal's multi-realms-per-KDC ability, but this was ill-conceived,
+ and AB has recently (6/09) ripped the last of that multi-realms
+ stuff out of samba4. AB says that in AD, it's not really possible
+ to make this work; several AD components structurally assume that
+ there's one realm per KDC. However, we do use this to support
+ canonicalization of realm-names: case variations, plus long-vs-short
+ variants of realm-names. No MIT porting task here, as long as MIT kdc
+ doesn't refuse to do some LDAP lookups (eg, alias' realm-name looks
+ wrong).
+* Heimdal supports multiple passwords on a client account: Samba4
+ seems to call hdb_next_enctype2key() in the pre-authentication
+ routines, to allow multiple passwords per account in krb5.
+ (I think this was intended to allow multiple salts). AD doesn't
+ support this, so the MIT port shouldn't bother with this.
+Not needed anymore, because MIT's code now handles PACs fully:
+* gss_krb5_copy_service_keyblock() (get the key used to actually
+ encrypt the ticket to the server, because the same key is used for
+ the PAC validation).
+* gsskrb5_extract_authtime_from_sec_context (get authtime from
+ kerberos ticket)
+* gsskrb5_extract_authz_data_from_sec_context (get authdata from
+ ticket, ie the PAC. Must unwrap the data if in an AD-IFRELEVANT)]
+Authz data extraction
+* We use krb5_ticket_get_authorization_data_type(), and expect
+ it to return the correct authz data, even if wrapped in an
+ AD-IFRELEVANT container. This doesn't need to be ported to MIT.
+ This should be obsoleted by MIT's new PAC code.
+libkdc
+* Samba4 needs to be built as a single binary (design requirement),
+ and this should include the KDC. Samba also (and perhaps more
+ importantly) needs to control the configuration environment of
+ the KDC.
+* But, libkdc doesn't matter for IPA; Samba invokes the Heimdal kdc
+ as a library call, but this is just a convenience, and the MIT
+ port can do otherwise w/o trouble.)
+Returned Salt for PreAuthentication
+ When the AD-KDC replies to pre-authentication, it returns the
+ salt, which may be in the form of a principalName that is in no
+ way connected with the current names. (ie, even if the
+ userPrincipalName and samAccountName are renamed, the old salt
+ is returned).
+ This is the kerberos standard salt, kept in the 'Key'. The
+ AD generation rules are found in a Mail from Luke Howard dated
+ 10 Nov 2004. The MIT glue layer doesn't really need to care about
+ these salt-handling details; the samba4 code& the LDAP backend
+ will conspire to make sure that MIT's KDC gets correct salts.
+ >
+ > From: Luke Howard<lukeh@padl.com>
+ > Organization: PADL Software Pty Ltd
+ > To: lukeh@padl.com
+ > Date: Wed, 10 Nov 2004 13:31:21 +1100
+ > Cc: huaraz@moeller.plus.com, samba-technical@lists.samba.org
+ > Subject: Re: Samba-3.0.7-1.3E Active Directory Issues
+ > -------
+ >
+ > Did some more testing, it appears the behaviour has another
+ > explanation. It appears that the standard Kerberos password salt
+ > algorithm is applied in Windows 2003, just that the source principal
+ > name is different.
+ >
+ > Here is what I've been able to deduce from creating a bunch of
+ > different accounts:
+ > [SAM name in this mail means the AD attribute samAccountName .
+ > E.g., jbob for a user and jbcomputer$ for a computer.]
+ >
+ > [UPN is the AD userPrincipalName attribute. For example, jbob@mydomain.com]
+ > Type of account Principal for Salting
+ > ========================================================================
+ > Computer Account host/<SAM-Name-Without-$>.realm@REALM
+ > User Account Without UPN<SAM-Name>@REALM
+ > User Account With UPN<LHS-Of-UPN>@REALM
+ >
+ > Note that if the computer account's SAM account name does not include
+ > the trailing '$', then the entire SAM account name is used as input to
+ > the salting principal. Setting a UPN for a computer account has no
+ > effect.
+ >
+ > It seems to me odd that the RHS of the UPN is not used in the salting
+ > principal. For example, a user with UPN foo@mydomain.com in the realm
+ > MYREALM.COM would have a salt of MYREALM.COMfoo. Perhaps this is to
+ > allow a user's UPN suffix to be changed without changing the salt. And
+ > perhaps using the UPN for salting signifies a move away SAM names and
+ > their associated constraints.
+ >
+ > For more information on how UPNs relate to the Kerberos protocol,
+ > see:
+ >
+ > http://www.ietf.org/proceedings/01dec/I-D/draft-ietf-krb-wg-kerberos-referrals-02.txt
+ >
+ > -- Luke
diff --git a/source4/auth/kerberos/kerberos.h b/source4/auth/kerberos/kerberos.h
new file mode 100644
index 0000000..5b13f56
--- /dev/null
+++ b/source4/auth/kerberos/kerberos.h
@@ -0,0 +1,89 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple kerberos5 routines for active directory
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Luke Howard 2002-2003
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _AUTH_KERBEROS_H_
+#define _AUTH_KERBEROS_H_
+
+#if defined(HAVE_KRB5)
+
+#include "system/kerberos.h"
+#include "auth/auth.h"
+#include "auth/kerberos/krb5_init_context.h"
+#include "librpc/gen_ndr/krb5pac.h"
+#include "lib/krb5_wrap/krb5_samba.h"
+
+struct auth_user_info_dc;
+struct cli_credentials;
+
+struct ccache_container {
+ struct smb_krb5_context *smb_krb5_context;
+ krb5_ccache ccache;
+};
+
+struct keytab_container {
+ struct smb_krb5_context *smb_krb5_context;
+ krb5_keytab keytab;
+ bool password_based;
+};
+
+/* not really ASN.1, but RFC 1964 */
+#define TOK_ID_KRB_AP_REQ ((const uint8_t *)"\x01\x00")
+#define TOK_ID_KRB_AP_REP ((const uint8_t *)"\x02\x00")
+#define TOK_ID_KRB_ERROR ((const uint8_t *)"\x03\x00")
+#define TOK_ID_GSS_GETMIC ((const uint8_t *)"\x01\x01")
+#define TOK_ID_GSS_WRAP ((const uint8_t *)"\x02\x01")
+
+#define ENC_ALL_TYPES (ENC_RC4_HMAC_MD5 | \
+ ENC_HMAC_SHA1_96_AES128 | ENC_HMAC_SHA1_96_AES256)
+
+#ifndef HAVE_KRB5_SET_DEFAULT_TGS_KTYPES
+krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc);
+#endif
+
+#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
+krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context, krb5_auth_context auth_context, krb5_keyblock *keyblock);
+#endif
+
+krb5_error_code smb_krb5_princ_component(krb5_context context,
+ krb5_const_principal principal,
+ int i,
+ krb5_data *data);
+
+/* Samba wrapper function for krb5 functionality. */
+ krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx,
+ struct PAC_DATA *pac_data,
+ krb5_context context,
+ const krb5_keyblock *krbtgt_keyblock,
+ const krb5_keyblock *service_keyblock,
+ DATA_BLOB *pac);
+ krb5_error_code kerberos_create_pac(TALLOC_CTX *mem_ctx,
+ struct auth_user_info_dc *user_info_dc,
+ krb5_context context,
+ const krb5_keyblock *krbtgt_keyblock,
+ const krb5_keyblock *service_keyblock,
+ krb5_principal client_principal,
+ time_t tgs_authtime,
+ DATA_BLOB *pac);
+
+#include "auth/kerberos/proto.h"
+
+#endif /* HAVE_KRB5 */
+
+#endif /* _AUTH_KERBEROS_H_ */
diff --git a/source4/auth/kerberos/kerberos_credentials.h b/source4/auth/kerberos/kerberos_credentials.h
new file mode 100644
index 0000000..9aeeb38
--- /dev/null
+++ b/source4/auth/kerberos/kerberos_credentials.h
@@ -0,0 +1,38 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Kerberos utility functions for GENSEC
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2010
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
+ struct cli_credentials *credentials,
+ struct smb_krb5_context *smb_krb5_context,
+ struct loadparm_context *lp_ctx,
+ struct tevent_context *event_ctx,
+ krb5_ccache ccache,
+ enum credentials_obtained *obtained,
+ const char **error_string);
+
+/* Manually prototyped here to avoid needing krb5 headers in most callers */
+krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
+ struct cli_credentials *credentials,
+ struct smb_krb5_context *smb_krb5_context,
+ krb5_principal *princ,
+ enum credentials_obtained *obtained,
+ const char **error_string);
diff --git a/source4/auth/kerberos/kerberos_pac.c b/source4/auth/kerberos/kerberos_pac.c
new file mode 100644
index 0000000..c33dc2f
--- /dev/null
+++ b/source4/auth/kerberos/kerberos_pac.c
@@ -0,0 +1,539 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Create and parse the krb5 PAC
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005,2008
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Luke Howard 2002-2003
+ Copyright (C) Stefan Metzmacher 2004-2005
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/kerberos.h"
+#include "auth/auth.h"
+#include "auth/kerberos/kerberos.h"
+#include "librpc/gen_ndr/ndr_krb5pac.h"
+#include <ldb.h>
+#include "auth/auth_sam_reply.h"
+#include "auth/credentials/credentials.h"
+#include "auth/kerberos/kerberos_util.h"
+#include "auth/kerberos/pac_utils.h"
+
+ krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx,
+ struct PAC_DATA *pac_data,
+ krb5_context context,
+ const krb5_keyblock *krbtgt_keyblock,
+ const krb5_keyblock *service_keyblock,
+ DATA_BLOB *pac)
+{
+ NTSTATUS nt_status;
+ krb5_error_code ret;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB zero_blob = data_blob(NULL, 0);
+ DATA_BLOB tmp_blob = data_blob(NULL, 0);
+ struct PAC_SIGNATURE_DATA *kdc_checksum = NULL;
+ struct PAC_SIGNATURE_DATA *srv_checksum = NULL;
+ uint32_t i;
+
+ /* First, just get the keytypes filled in (and lengths right, eventually) */
+ for (i=0; i < pac_data->num_buffers; i++) {
+ if (pac_data->buffers[i].type != PAC_TYPE_KDC_CHECKSUM) {
+ continue;
+ }
+ kdc_checksum = &pac_data->buffers[i].info->kdc_cksum,
+ ret = smb_krb5_make_pac_checksum(mem_ctx,
+ &zero_blob,
+ context,
+ krbtgt_keyblock,
+ &kdc_checksum->type,
+ &kdc_checksum->signature);
+ if (ret) {
+ DEBUG(2, ("making krbtgt PAC checksum failed: %s\n",
+ smb_get_krb5_error_message(context, ret, mem_ctx)));
+ talloc_free(pac_data);
+ return ret;
+ }
+ }
+
+ for (i=0; i < pac_data->num_buffers; i++) {
+ if (pac_data->buffers[i].type != PAC_TYPE_SRV_CHECKSUM) {
+ continue;
+ }
+ srv_checksum = &pac_data->buffers[i].info->srv_cksum;
+ ret = smb_krb5_make_pac_checksum(mem_ctx,
+ &zero_blob,
+ context,
+ service_keyblock,
+ &srv_checksum->type,
+ &srv_checksum->signature);
+ if (ret) {
+ DEBUG(2, ("making service PAC checksum failed: %s\n",
+ smb_get_krb5_error_message(context, ret, mem_ctx)));
+ talloc_free(pac_data);
+ return ret;
+ }
+ }
+
+ if (!kdc_checksum) {
+ DEBUG(2, ("Invalid PAC constructed for signing, no KDC checksum present!\n"));
+ return EINVAL;
+ }
+ if (!srv_checksum) {
+ DEBUG(2, ("Invalid PAC constructed for signing, no SRV checksum present!\n"));
+ return EINVAL;
+ }
+
+ /* But wipe out the actual signatures */
+ memset(kdc_checksum->signature.data, '\0', kdc_checksum->signature.length);
+ memset(srv_checksum->signature.data, '\0', srv_checksum->signature.length);
+
+ ndr_err = ndr_push_struct_blob(&tmp_blob, mem_ctx,
+ pac_data,
+ (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ nt_status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(1, ("PAC (presig) push failed: %s\n", nt_errstr(nt_status)));
+ talloc_free(pac_data);
+ return EINVAL;
+ }
+
+ /* Then sign the result of the previous push, where the sig was zero'ed out */
+ ret = smb_krb5_make_pac_checksum(mem_ctx,
+ &tmp_blob,
+ context,
+ service_keyblock,
+ &srv_checksum->type,
+ &srv_checksum->signature);
+
+ if (ret) {
+ DBG_WARNING("making krbtgt PAC srv_checksum failed: %s\n",
+ smb_get_krb5_error_message(context, ret, mem_ctx));
+ talloc_free(pac_data);
+ return ret;
+ }
+
+ /* Then sign Server checksum */
+ ret = smb_krb5_make_pac_checksum(mem_ctx,
+ &srv_checksum->signature,
+ context,
+ krbtgt_keyblock,
+ &kdc_checksum->type,
+ &kdc_checksum->signature);
+ if (ret) {
+ DBG_WARNING("making krbtgt PAC kdc_checksum failed: %s\n",
+ smb_get_krb5_error_message(context, ret, mem_ctx));
+ talloc_free(pac_data);
+ return ret;
+ }
+
+ /* And push it out again, this time to the world. This relies on deterministic pointer values */
+ ndr_err = ndr_push_struct_blob(&tmp_blob, mem_ctx,
+ pac_data,
+ (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ nt_status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(1, ("PAC (final) push failed: %s\n", nt_errstr(nt_status)));
+ talloc_free(pac_data);
+ return EINVAL;
+ }
+
+ *pac = tmp_blob;
+
+ return ret;
+}
+
+
+ krb5_error_code kerberos_create_pac(TALLOC_CTX *mem_ctx,
+ struct auth_user_info_dc *user_info_dc,
+ krb5_context context,
+ const krb5_keyblock *krbtgt_keyblock,
+ const krb5_keyblock *service_keyblock,
+ krb5_principal client_principal,
+ time_t tgs_authtime,
+ DATA_BLOB *pac)
+{
+ NTSTATUS nt_status;
+ krb5_error_code ret;
+ struct PAC_DATA *pac_data = talloc(mem_ctx, struct PAC_DATA);
+ struct netr_SamInfo3 *sam3;
+ union PAC_INFO *u_LOGON_INFO;
+ struct PAC_LOGON_INFO *LOGON_INFO;
+ union PAC_INFO *u_LOGON_NAME;
+ struct PAC_LOGON_NAME *LOGON_NAME;
+ union PAC_INFO *u_KDC_CHECKSUM;
+ union PAC_INFO *u_SRV_CHECKSUM;
+
+ char *name;
+
+ enum {
+ PAC_BUF_LOGON_INFO = 0,
+ PAC_BUF_LOGON_NAME = 1,
+ PAC_BUF_SRV_CHECKSUM = 2,
+ PAC_BUF_KDC_CHECKSUM = 3,
+ PAC_BUF_NUM_BUFFERS = 4
+ };
+
+ if (!pac_data) {
+ return ENOMEM;
+ }
+
+ pac_data->num_buffers = PAC_BUF_NUM_BUFFERS;
+ pac_data->version = 0;
+
+ pac_data->buffers = talloc_array(pac_data,
+ struct PAC_BUFFER,
+ pac_data->num_buffers);
+ if (!pac_data->buffers) {
+ talloc_free(pac_data);
+ return ENOMEM;
+ }
+
+ /* LOGON_INFO */
+ u_LOGON_INFO = talloc_zero(pac_data->buffers, union PAC_INFO);
+ if (!u_LOGON_INFO) {
+ talloc_free(pac_data);
+ return ENOMEM;
+ }
+ pac_data->buffers[PAC_BUF_LOGON_INFO].type = PAC_TYPE_LOGON_INFO;
+ pac_data->buffers[PAC_BUF_LOGON_INFO].info = u_LOGON_INFO;
+
+ /* LOGON_NAME */
+ u_LOGON_NAME = talloc_zero(pac_data->buffers, union PAC_INFO);
+ if (!u_LOGON_NAME) {
+ talloc_free(pac_data);
+ return ENOMEM;
+ }
+ pac_data->buffers[PAC_BUF_LOGON_NAME].type = PAC_TYPE_LOGON_NAME;
+ pac_data->buffers[PAC_BUF_LOGON_NAME].info = u_LOGON_NAME;
+ LOGON_NAME = &u_LOGON_NAME->logon_name;
+
+ /* SRV_CHECKSUM */
+ u_SRV_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO);
+ if (!u_SRV_CHECKSUM) {
+ talloc_free(pac_data);
+ return ENOMEM;
+ }
+ pac_data->buffers[PAC_BUF_SRV_CHECKSUM].type = PAC_TYPE_SRV_CHECKSUM;
+ pac_data->buffers[PAC_BUF_SRV_CHECKSUM].info = u_SRV_CHECKSUM;
+
+ /* KDC_CHECKSUM */
+ u_KDC_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO);
+ if (!u_KDC_CHECKSUM) {
+ talloc_free(pac_data);
+ return ENOMEM;
+ }
+ pac_data->buffers[PAC_BUF_KDC_CHECKSUM].type = PAC_TYPE_KDC_CHECKSUM;
+ pac_data->buffers[PAC_BUF_KDC_CHECKSUM].info = u_KDC_CHECKSUM;
+
+ /* now the real work begins... */
+
+ LOGON_INFO = talloc_zero(u_LOGON_INFO, struct PAC_LOGON_INFO);
+ if (!LOGON_INFO) {
+ talloc_free(pac_data);
+ return ENOMEM;
+ }
+ nt_status = auth_convert_user_info_dc_saminfo3(LOGON_INFO, user_info_dc,
+ AUTH_INCLUDE_RESOURCE_GROUPS,
+ &sam3, NULL);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("Getting Samba info failed: %s\n", nt_errstr(nt_status)));
+ talloc_free(pac_data);
+ return EINVAL;
+ }
+
+ u_LOGON_INFO->logon_info.info = LOGON_INFO;
+ LOGON_INFO->info3 = *sam3;
+
+ ret = krb5_unparse_name_flags(context, client_principal,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM |
+ KRB5_PRINCIPAL_UNPARSE_DISPLAY,
+ &name);
+ if (ret) {
+ return ret;
+ }
+ LOGON_NAME->account_name = talloc_strdup(LOGON_NAME, name);
+ free(name);
+ /*
+ this logon_time field is absolutely critical. This is what
+ caused all our PAC troubles :-)
+ */
+ unix_to_nt_time(&LOGON_NAME->logon_time, tgs_authtime);
+
+ ret = kerberos_encode_pac(mem_ctx,
+ pac_data,
+ context,
+ krbtgt_keyblock,
+ service_keyblock,
+ pac);
+ talloc_free(pac_data);
+ return ret;
+}
+
+static krb5_error_code kerberos_pac_buffer_present(krb5_context context,
+ const krb5_const_pac pac,
+ uint32_t type)
+{
+#ifdef SAMBA4_USES_HEIMDAL
+ return krb5_pac_get_buffer(context, pac, type, NULL);
+#else /* MIT */
+ krb5_error_code ret;
+ krb5_data data;
+
+ /*
+ * MIT won't let us pass NULL for the data parameter, so we are forced
+ * to allocate a new buffer and then immediately free it.
+ */
+ ret = krb5_pac_get_buffer(context, pac, type, &data);
+ if (ret == 0) {
+ krb5_free_data_contents(context, &data);
+ }
+ return ret;
+#endif /* SAMBA4_USES_HEIMDAL */
+}
+
+krb5_error_code kerberos_pac_to_user_info_dc(TALLOC_CTX *mem_ctx,
+ krb5_const_pac pac,
+ krb5_context context,
+ struct auth_user_info_dc **user_info_dc,
+ const enum auth_group_inclusion group_inclusion,
+ struct PAC_SIGNATURE_DATA *pac_srv_sig,
+ struct PAC_SIGNATURE_DATA *pac_kdc_sig,
+ struct PAC_DOMAIN_GROUP_MEMBERSHIP **resource_groups)
+{
+ NTSTATUS nt_status;
+ enum ndr_err_code ndr_err;
+ krb5_error_code ret;
+
+ DATA_BLOB pac_logon_info_in, pac_srv_checksum_in, pac_kdc_checksum_in;
+ krb5_data k5pac_logon_info_in, k5pac_srv_checksum_in, k5pac_kdc_checksum_in;
+ DATA_BLOB pac_upn_dns_info_in;
+ krb5_data k5pac_upn_dns_info_in;
+
+ union PAC_INFO info;
+ union PAC_INFO _upn_dns_info;
+ struct PAC_UPN_DNS_INFO *upn_dns_info = NULL;
+ struct auth_user_info_dc *user_info_dc_out;
+ const struct PAC_DOMAIN_GROUP_MEMBERSHIP *resource_groups_in = NULL;
+ struct PAC_DOMAIN_GROUP_MEMBERSHIP *resource_groups_out = NULL;
+
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ if (pac == NULL) {
+ talloc_free(tmp_ctx);
+ return EINVAL;
+ }
+
+ ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_LOGON_INFO, &k5pac_logon_info_in);
+ if (ret != 0) {
+ talloc_free(tmp_ctx);
+ return EINVAL;
+ }
+
+ pac_logon_info_in = data_blob_const(k5pac_logon_info_in.data, k5pac_logon_info_in.length);
+
+ ndr_err = ndr_pull_union_blob(&pac_logon_info_in, tmp_ctx, &info,
+ PAC_TYPE_LOGON_INFO,
+ (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO);
+ smb_krb5_free_data_contents(context, &k5pac_logon_info_in);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ nt_status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(0,("can't parse the PAC LOGON_INFO: %s\n", nt_errstr(nt_status)));
+ talloc_free(tmp_ctx);
+ return EINVAL;
+ }
+ if (info.logon_info.info == NULL) {
+ DEBUG(0,("can't parse the PAC LOGON_INFO: missing info pointer\n"));
+ talloc_free(tmp_ctx);
+ return EINVAL;
+ }
+
+ ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_UPN_DNS_INFO,
+ &k5pac_upn_dns_info_in);
+ if (ret == ENOENT) {
+ ZERO_STRUCT(k5pac_upn_dns_info_in);
+ ret = 0;
+ }
+ if (ret != 0) {
+ talloc_free(tmp_ctx);
+ return EINVAL;
+ }
+
+ pac_upn_dns_info_in = data_blob_const(k5pac_upn_dns_info_in.data,
+ k5pac_upn_dns_info_in.length);
+
+ if (pac_upn_dns_info_in.length != 0) {
+ ndr_err = ndr_pull_union_blob(&pac_upn_dns_info_in, tmp_ctx,
+ &_upn_dns_info,
+ PAC_TYPE_UPN_DNS_INFO,
+ (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO);
+ smb_krb5_free_data_contents(context, &k5pac_upn_dns_info_in);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ nt_status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(0,("can't parse the PAC UPN_DNS_INFO: %s\n",
+ nt_errstr(nt_status)));
+ talloc_free(tmp_ctx);
+ return EINVAL;
+ }
+ upn_dns_info = &_upn_dns_info.upn_dns_info;
+ }
+
+ /* Pull this right into the normal auth system structures */
+ nt_status = make_user_info_dc_pac(tmp_ctx,
+ info.logon_info.info,
+ upn_dns_info,
+ group_inclusion,
+ &user_info_dc_out);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DBG_ERR("make_user_info_dc_pac() failed - %s\n",
+ nt_errstr(nt_status));
+ NDR_PRINT_DEBUG(PAC_LOGON_INFO, info.logon_info.info);
+ if (upn_dns_info != NULL) {
+ NDR_PRINT_DEBUG(PAC_UPN_DNS_INFO, upn_dns_info);
+ }
+ talloc_free(tmp_ctx);
+ return EINVAL;
+ }
+
+ if (pac_srv_sig) {
+ ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_SRV_CHECKSUM, &k5pac_srv_checksum_in);
+ if (ret != 0) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ pac_srv_checksum_in = data_blob_const(k5pac_srv_checksum_in.data, k5pac_srv_checksum_in.length);
+
+ ndr_err = ndr_pull_struct_blob(&pac_srv_checksum_in, pac_srv_sig,
+ pac_srv_sig,
+ (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
+ smb_krb5_free_data_contents(context, &k5pac_srv_checksum_in);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ nt_status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(0,("can't parse the server signature: %s\n",
+ nt_errstr(nt_status)));
+ return EINVAL;
+ }
+ }
+
+ if (pac_kdc_sig) {
+ ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_KDC_CHECKSUM, &k5pac_kdc_checksum_in);
+ if (ret != 0) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ pac_kdc_checksum_in = data_blob_const(k5pac_kdc_checksum_in.data, k5pac_kdc_checksum_in.length);
+
+ ndr_err = ndr_pull_struct_blob(&pac_kdc_checksum_in, pac_kdc_sig,
+ pac_kdc_sig,
+ (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
+ smb_krb5_free_data_contents(context, &k5pac_kdc_checksum_in);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ nt_status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(0,("can't parse the KDC signature: %s\n",
+ nt_errstr(nt_status)));
+ return EINVAL;
+ }
+ }
+
+ /*
+ * Based on the presence of a REQUESTER_SID PAC buffer, ascertain
+ * whether the ticket is a TGT. This helps the KDC and kpasswd service
+ * ensure they do not accept tickets meant for the other.
+ *
+ * This heuristic will fail for older Samba versions and Windows prior
+ * to Nov. 2021 updates, which lack support for the REQUESTER_SID PAC
+ * buffer.
+ */
+ ret = kerberos_pac_buffer_present(context, pac, PAC_TYPE_REQUESTER_SID);
+ if (ret == ENOENT) {
+ /* This probably isn't a TGT. */
+ user_info_dc_out->ticket_type = TICKET_TYPE_NON_TGT;
+ } else if (ret != 0) {
+ talloc_free(tmp_ctx);
+ return ret;
+ } else {
+ /* This probably is a TGT. */
+ user_info_dc_out->ticket_type = TICKET_TYPE_TGT;
+ }
+
+ /*
+ * If we have resource groups and the caller wants them returned, we
+ * oblige.
+ */
+ resource_groups_in = &info.logon_info.info->resource_groups;
+ if (resource_groups != NULL && resource_groups_in->groups.count != 0) {
+ resource_groups_out = talloc(tmp_ctx, struct PAC_DOMAIN_GROUP_MEMBERSHIP);
+ if (resource_groups_out == NULL) {
+ talloc_free(tmp_ctx);
+ return ENOMEM;
+ }
+
+ *resource_groups_out = (struct PAC_DOMAIN_GROUP_MEMBERSHIP) {
+ .domain_sid = talloc_steal(resource_groups_out, resource_groups_in->domain_sid),
+ .groups = {
+ .count = resource_groups_in->groups.count,
+ .rids = talloc_steal(resource_groups_out, resource_groups_in->groups.rids),
+ },
+ };
+ }
+
+ *user_info_dc = talloc_steal(mem_ctx, user_info_dc_out);
+ if (resource_groups_out != NULL) {
+ *resource_groups = talloc_steal(mem_ctx, resource_groups_out);
+ }
+
+ return 0;
+}
+
+
+NTSTATUS kerberos_pac_blob_to_user_info_dc(TALLOC_CTX *mem_ctx,
+ DATA_BLOB pac_blob,
+ krb5_context context,
+ struct auth_user_info_dc **user_info_dc,
+ struct PAC_SIGNATURE_DATA *pac_srv_sig,
+ struct PAC_SIGNATURE_DATA *pac_kdc_sig)
+{
+ krb5_error_code ret;
+ krb5_pac pac;
+ ret = krb5_pac_parse(context,
+ pac_blob.data, pac_blob.length,
+ &pac);
+ if (ret) {
+ return map_nt_error_from_unix_common(ret);
+ }
+
+
+ ret = kerberos_pac_to_user_info_dc(mem_ctx,
+ pac,
+ context,
+ user_info_dc,
+ AUTH_INCLUDE_RESOURCE_GROUPS,
+ pac_srv_sig,
+ pac_kdc_sig,
+ NULL);
+ krb5_pac_free(context, pac);
+ if (ret) {
+ return map_nt_error_from_unix_common(ret);
+ }
+ return NT_STATUS_OK;
+}
diff --git a/source4/auth/kerberos/kerberos_util.c b/source4/auth/kerberos/kerberos_util.c
new file mode 100644
index 0000000..ba8d822
--- /dev/null
+++ b/source4/auth/kerberos/kerberos_util.c
@@ -0,0 +1,721 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Kerberos utility functions for GENSEC
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_krb5.h"
+#include "auth/kerberos/kerberos_credentials.h"
+#include "auth/kerberos/kerberos_util.h"
+
+struct principal_container {
+ struct smb_krb5_context *smb_krb5_context;
+ krb5_principal principal;
+ const char *string_form; /* Optional */
+};
+
+static krb5_error_code free_principal(struct principal_container *pc)
+{
+ /* current heimdal - 0.6.3, which we need anyway, fixes segfaults here */
+ krb5_free_principal(pc->smb_krb5_context->krb5_context, pc->principal);
+
+ return 0;
+}
+
+
+static krb5_error_code parse_principal(TALLOC_CTX *parent_ctx,
+ const char *princ_string,
+ struct smb_krb5_context *smb_krb5_context,
+ krb5_principal *princ,
+ const char **error_string)
+{
+ int ret;
+ struct principal_container *mem_ctx;
+ if (princ_string == NULL) {
+ *princ = NULL;
+ return 0;
+ }
+
+ /*
+ * Start with talloc(), talloc_reference() and only then call
+ * krb5_parse_name(). If any of them fails, the cleanup code is simpler.
+ */
+ mem_ctx = talloc(parent_ctx, struct principal_container);
+ if (!mem_ctx) {
+ (*error_string) = error_message(ENOMEM);
+ return ENOMEM;
+ }
+
+ mem_ctx->smb_krb5_context = talloc_reference(mem_ctx,
+ smb_krb5_context);
+ if (mem_ctx->smb_krb5_context == NULL) {
+ (*error_string) = error_message(ENOMEM);
+ talloc_free(mem_ctx);
+ return ENOMEM;
+ }
+
+ ret = krb5_parse_name(smb_krb5_context->krb5_context,
+ princ_string, princ);
+
+ if (ret) {
+ (*error_string) = smb_get_krb5_error_message(
+ smb_krb5_context->krb5_context,
+ ret, parent_ctx);
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ /* This song-and-dance effectively puts the principal
+ * into talloc, so we can't lose it. */
+ mem_ctx->principal = *princ;
+ talloc_set_destructor(mem_ctx, free_principal);
+ return 0;
+}
+
+/* Obtain the principal set on this context. Requires a
+ * smb_krb5_context because we are doing krb5 principal parsing with
+ * the library routines. The returned princ is placed in the talloc
+ * system by means of a destructor (do *not* free). */
+
+krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
+ struct cli_credentials *credentials,
+ struct smb_krb5_context *smb_krb5_context,
+ krb5_principal *princ,
+ enum credentials_obtained *obtained,
+ const char **error_string)
+{
+ krb5_error_code ret;
+ const char *princ_string;
+ TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
+ *obtained = CRED_UNINITIALISED;
+
+ if (!mem_ctx) {
+ (*error_string) = error_message(ENOMEM);
+ return ENOMEM;
+ }
+ princ_string = cli_credentials_get_principal_and_obtained(credentials,
+ mem_ctx,
+ obtained);
+ if (!princ_string) {
+ *princ = NULL;
+ return 0;
+ }
+
+ ret = parse_principal(parent_ctx, princ_string,
+ smb_krb5_context, princ, error_string);
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/* Obtain the principal set on this context. Requires a
+ * smb_krb5_context because we are doing krb5 principal parsing with
+ * the library routines. The returned princ is placed in the talloc
+ * system by means of a destructor (do *not* free). */
+
+static krb5_error_code impersonate_principal_from_credentials(
+ TALLOC_CTX *parent_ctx,
+ struct cli_credentials *credentials,
+ struct smb_krb5_context *smb_krb5_context,
+ krb5_principal *princ,
+ const char **error_string)
+{
+ return parse_principal(parent_ctx,
+ cli_credentials_get_impersonate_principal(credentials),
+ smb_krb5_context, princ, error_string);
+}
+
+krb5_error_code smb_krb5_create_principals_array(TALLOC_CTX *mem_ctx,
+ krb5_context context,
+ const char *account_name,
+ const char *realm,
+ uint32_t num_spns,
+ const char *spns[],
+ uint32_t *pnum_principals,
+ krb5_principal **pprincipals,
+ const char **error_string)
+{
+ krb5_error_code code;
+ TALLOC_CTX *tmp_ctx;
+ uint32_t num_principals = 0;
+ krb5_principal *principals;
+ uint32_t i;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ *error_string = "Cannot allocate tmp_ctx";
+ return ENOMEM;
+ }
+
+ if (realm == NULL) {
+ *error_string = "Cannot create principal without a realm";
+ code = EINVAL;
+ goto done;
+ }
+
+ if (account_name == NULL && (num_spns == 0 || spns == NULL)) {
+ *error_string = "Cannot create principal without an account or SPN";
+ code = EINVAL;
+ goto done;
+ }
+
+ if (account_name != NULL && account_name[0] != '\0') {
+ num_principals++;
+ }
+ num_principals += num_spns;
+
+ principals = talloc_zero_array(tmp_ctx,
+ krb5_principal,
+ num_principals);
+ if (principals == NULL) {
+ *error_string = "Cannot allocate principals";
+ code = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < num_spns; i++) {
+ code = krb5_parse_name(context, spns[i], &(principals[i]));
+ if (code != 0) {
+ *error_string = smb_get_krb5_error_message(context,
+ code,
+ mem_ctx);
+ goto done;
+ }
+ }
+
+ if (account_name != NULL && account_name[0] != '\0') {
+ code = smb_krb5_make_principal(context,
+ &(principals[i]),
+ realm,
+ account_name,
+ NULL);
+ if (code != 0) {
+ *error_string = smb_get_krb5_error_message(context,
+ code,
+ mem_ctx);
+ goto done;
+ }
+ }
+
+ if (pnum_principals != NULL) {
+ *pnum_principals = num_principals;
+
+ if (pprincipals != NULL) {
+ *pprincipals = talloc_steal(mem_ctx, principals);
+ }
+ }
+
+ code = 0;
+done:
+ talloc_free(tmp_ctx);
+ return code;
+}
+
+/**
+ * Return a freshly allocated ccache (destroyed by destructor on child
+ * of parent_ctx), for a given set of client credentials
+ */
+
+ krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
+ struct cli_credentials *credentials,
+ struct smb_krb5_context *smb_krb5_context,
+ struct loadparm_context *lp_ctx,
+ struct tevent_context *event_ctx,
+ krb5_ccache ccache,
+ enum credentials_obtained *obtained,
+ const char **error_string)
+{
+ krb5_error_code ret;
+ const char *password;
+ const char *self_service;
+ const char *target_service;
+ time_t kdc_time = 0;
+ krb5_principal princ;
+ krb5_principal impersonate_principal;
+ int tries;
+ TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
+ krb5_get_init_creds_opt *krb_options;
+ struct cli_credentials *fast_creds;
+
+ if (!mem_ctx) {
+ (*error_string) = strerror(ENOMEM);
+ return ENOMEM;
+ }
+
+ ret = principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &princ, obtained, error_string);
+ if (ret) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ if (princ == NULL) {
+ (*error_string) = talloc_asprintf(credentials, "principal, username or realm was not specified in the credentials");
+ talloc_free(mem_ctx);
+ return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
+ }
+
+ ret = impersonate_principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &impersonate_principal, error_string);
+ if (ret) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ self_service = cli_credentials_get_self_service(credentials);
+ target_service = cli_credentials_get_target_service(credentials);
+
+ password = cli_credentials_get_password(credentials);
+
+ /* setup the krb5 options we want */
+ if ((ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, &krb_options))) {
+ (*error_string) = talloc_asprintf(credentials, "krb5_get_init_creds_opt_alloc failed (%s)\n",
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+#ifdef SAMBA4_USES_HEIMDAL /* Disable for now MIT reads defaults when needed */
+ /* get the defaults */
+ krb5_get_init_creds_opt_set_default_flags(smb_krb5_context->krb5_context, NULL, NULL, krb_options);
+#endif
+ /* set if we want a forwardable ticket */
+ switch (cli_credentials_get_krb_forwardable(credentials)) {
+ case CRED_AUTO_KRB_FORWARDABLE:
+ break;
+ case CRED_NO_KRB_FORWARDABLE:
+ krb5_get_init_creds_opt_set_forwardable(krb_options, FALSE);
+ break;
+ case CRED_FORCE_KRB_FORWARDABLE:
+ krb5_get_init_creds_opt_set_forwardable(krb_options, TRUE);
+ break;
+ }
+
+#ifdef SAMBA4_USES_HEIMDAL /* FIXME: MIT does not have this yet */
+ /*
+ * In order to work against windows KDCs even if we use
+ * the netbios domain name as realm, we need to add the following
+ * flags:
+ * KRB5_INIT_CREDS_NO_C_CANON_CHECK;
+ * KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK;
+ *
+ * On MIT: Set pkinit_eku_checking to none
+ */
+ krb5_get_init_creds_opt_set_win2k(smb_krb5_context->krb5_context,
+ krb_options, true);
+ krb5_get_init_creds_opt_set_canonicalize(smb_krb5_context->krb5_context,
+ krb_options, true);
+#else /* MIT */
+ krb5_get_init_creds_opt_set_canonicalize(krb_options, true);
+#endif
+
+ fast_creds = cli_credentials_get_krb5_fast_armor_credentials(credentials);
+
+ if (fast_creds != NULL) {
+#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_CCACHE
+ struct ccache_container *fast_ccc = NULL;
+ const char *fast_error_string = NULL;
+ ret = cli_credentials_get_ccache(fast_creds, event_ctx, lp_ctx, &fast_ccc, &fast_error_string);
+ if (ret != 0) {
+ (*error_string) = talloc_asprintf(credentials,
+ "Obtaining the Kerberos FAST armor credentials failed: %s\n",
+ fast_error_string);
+ return ret;
+ }
+ krb5_get_init_creds_opt_set_fast_ccache(smb_krb5_context->krb5_context,
+ krb_options,
+ fast_ccc->ccache);
+#else
+ *error_string = talloc_strdup(credentials,
+ "Using Kerberos FAST "
+ "armor credentials not possible "
+ "with this Kerberos library. "
+ "Modern MIT or Samba's embedded "
+ "Heimdal required");
+ return EINVAL;
+#endif
+ }
+
+#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_FAST_FLAGS
+ {
+ bool require_fast;
+ /*
+ * This ensures that if FAST was required, but no armor
+ * credentials cache was specified, we proceed with (eg)
+ * anonymous PKINIT
+ */
+ require_fast = cli_credentials_get_krb5_require_fast_armor(credentials);
+ if (require_fast) {
+ krb5_get_init_creds_opt_set_fast_flags(smb_krb5_context->krb5_context,
+ krb_options,
+ KRB5_FAST_REQUIRED);
+ }
+ }
+#endif
+
+ tries = 2;
+ while (tries--) {
+#ifdef SAMBA4_USES_HEIMDAL
+ struct tevent_context *previous_ev;
+ /* Do this every time, in case we have weird recursive issues here */
+ ret = smb_krb5_context_set_event_ctx(smb_krb5_context, event_ctx, &previous_ev);
+ if (ret) {
+ talloc_free(mem_ctx);
+ krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
+ return ret;
+ }
+#endif
+ if (password) {
+ if (impersonate_principal) {
+ ret = smb_krb5_kinit_s4u2_ccache(smb_krb5_context->krb5_context,
+ ccache,
+ princ,
+ password,
+ impersonate_principal,
+ self_service,
+ target_service,
+ krb_options,
+ NULL,
+ &kdc_time);
+ } else {
+ ret = smb_krb5_kinit_password_ccache(smb_krb5_context->krb5_context,
+ ccache,
+ princ,
+ password,
+ target_service,
+ krb_options,
+ NULL,
+ &kdc_time);
+ }
+ } else if (impersonate_principal) {
+ talloc_free(mem_ctx);
+ (*error_string) = "INTERNAL error: Cannot impersonate principal with just a keyblock. A password must be specified in the credentials";
+ krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
+#ifdef SAMBA4_USES_HEIMDAL
+ smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
+#endif
+ return EINVAL;
+ } else {
+ /* No password available, try to use a keyblock instead */
+
+ krb5_keyblock keyblock;
+ const struct samr_Password *mach_pwd;
+ mach_pwd = cli_credentials_get_nt_hash(credentials, mem_ctx);
+ if (!mach_pwd) {
+ talloc_free(mem_ctx);
+ (*error_string) = "kinit_to_ccache: No password available for kinit\n";
+ krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
+#ifdef SAMBA4_USES_HEIMDAL
+ smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
+#endif
+ return EINVAL;
+ }
+ ret = smb_krb5_keyblock_init_contents(smb_krb5_context->krb5_context,
+ ENCTYPE_ARCFOUR_HMAC,
+ mach_pwd->hash, sizeof(mach_pwd->hash),
+ &keyblock);
+
+ if (ret == 0) {
+ ret = smb_krb5_kinit_keyblock_ccache(smb_krb5_context->krb5_context,
+ ccache,
+ princ,
+ &keyblock,
+ target_service,
+ krb_options,
+ NULL,
+ &kdc_time);
+ krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &keyblock);
+ }
+ }
+
+#ifdef SAMBA4_USES_HEIMDAL
+ smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
+#endif
+
+ if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
+ /* Perhaps we have been given an invalid skew, so try again without it */
+ time_t t = time(NULL);
+ krb5_set_real_time(smb_krb5_context->krb5_context, t, 0);
+ } else {
+ /* not a skew problem */
+ break;
+ }
+ }
+
+ krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
+
+ if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
+ (*error_string) = talloc_asprintf(credentials, "kinit for %s failed (%s)\n",
+ cli_credentials_get_principal(credentials, mem_ctx),
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ /* cope with ticket being in the future due to clock skew */
+ if ((unsigned)kdc_time > time(NULL)) {
+ time_t t = time(NULL);
+ int time_offset =(unsigned)kdc_time-t;
+ DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
+ krb5_set_real_time(smb_krb5_context->krb5_context, t + time_offset + 1, 0);
+ }
+
+ if (ret == KRB5KDC_ERR_PREAUTH_FAILED && cli_credentials_wrong_password(credentials)) {
+ ret = kinit_to_ccache(parent_ctx,
+ credentials,
+ smb_krb5_context,
+ lp_ctx,
+ event_ctx,
+ ccache, obtained,
+ error_string);
+ }
+
+ if (ret) {
+ (*error_string) = talloc_asprintf(credentials, "kinit for %s failed (%s)\n",
+ cli_credentials_get_principal(credentials, mem_ctx),
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ DEBUG(10,("kinit for %s succeeded\n",
+ cli_credentials_get_principal(credentials, mem_ctx)));
+
+
+ talloc_free(mem_ctx);
+ return 0;
+}
+
+static krb5_error_code free_keytab_container(struct keytab_container *ktc)
+{
+ return krb5_kt_close(ktc->smb_krb5_context->krb5_context, ktc->keytab);
+}
+
+krb5_error_code smb_krb5_get_keytab_container(TALLOC_CTX *mem_ctx,
+ struct smb_krb5_context *smb_krb5_context,
+ krb5_keytab opt_keytab,
+ const char *keytab_name,
+ struct keytab_container **ktc)
+{
+ krb5_keytab keytab;
+ krb5_error_code ret;
+
+ /*
+ * Start with talloc(), talloc_reference() and only then call
+ * krb5_kt_resolve(). If any of them fails, the cleanup code is simpler.
+ */
+ *ktc = talloc(mem_ctx, struct keytab_container);
+ if (!*ktc) {
+ return ENOMEM;
+ }
+
+ (*ktc)->smb_krb5_context = talloc_reference(*ktc, smb_krb5_context);
+ if ((*ktc)->smb_krb5_context == NULL) {
+ TALLOC_FREE(*ktc);
+ return ENOMEM;
+ }
+
+ if (opt_keytab) {
+ keytab = opt_keytab;
+ } else {
+ ret = krb5_kt_resolve(smb_krb5_context->krb5_context,
+ keytab_name, &keytab);
+ if (ret) {
+ DEBUG(1,("failed to open krb5 keytab: %s\n",
+ smb_get_krb5_error_message(
+ smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+ TALLOC_FREE(*ktc);
+ return ret;
+ }
+ }
+
+ (*ktc)->keytab = keytab;
+ (*ktc)->password_based = false;
+ talloc_set_destructor(*ktc, free_keytab_container);
+
+ return 0;
+}
+
+/*
+ * Walk the keytab, looking for entries of this principal name,
+ * with KVNO other than current kvno -1.
+ *
+ * These entries are now stale,
+ * we only keep the current and previous entries around.
+ *
+ * Inspired by the code in Samba3 for 'use kerberos keytab'.
+ */
+krb5_error_code smb_krb5_remove_obsolete_keytab_entries(TALLOC_CTX *mem_ctx,
+ krb5_context context,
+ krb5_keytab keytab,
+ uint32_t num_principals,
+ krb5_principal *principals,
+ krb5_kvno kvno,
+ bool *found_previous,
+ const char **error_string)
+{
+ TALLOC_CTX *tmp_ctx;
+ krb5_error_code code;
+ krb5_kt_cursor cursor;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ *error_string = "Cannot allocate tmp_ctx";
+ return ENOMEM;
+ }
+
+ *found_previous = true;
+
+ code = krb5_kt_start_seq_get(context, keytab, &cursor);
+ switch (code) {
+ case 0:
+ break;
+#ifdef HEIM_ERR_OPNOTSUPP
+ case HEIM_ERR_OPNOTSUPP:
+#endif
+ case ENOENT:
+ case KRB5_KT_END:
+ /* no point enumerating if there isn't anything here */
+ code = 0;
+ goto done;
+ default:
+ *error_string = talloc_asprintf(mem_ctx,
+ "failed to open keytab for read of old entries: %s\n",
+ smb_get_krb5_error_message(context, code, mem_ctx));
+ goto done;
+ }
+
+ do {
+ krb5_kvno old_kvno = kvno - 1;
+ krb5_keytab_entry entry;
+ bool matched = false;
+ uint32_t i;
+
+ code = krb5_kt_next_entry(context, keytab, &entry, &cursor);
+ if (code) {
+ break;
+ }
+
+ for (i = 0; i < num_principals; i++) {
+ krb5_boolean ok;
+
+ ok = smb_krb5_kt_compare(context,
+ &entry,
+ principals[i],
+ 0,
+ 0);
+ if (ok) {
+ matched = true;
+ break;
+ }
+ }
+
+ if (!matched) {
+ /*
+ * Free the entry, it wasn't the one we were looking
+ * for anyway
+ */
+ krb5_kt_free_entry(context, &entry);
+ /* Make sure we do not double free */
+ ZERO_STRUCT(entry);
+ continue;
+ }
+
+ /*
+ * Delete it, if it is not kvno - 1.
+ *
+ * Some keytab files store the kvno only in 8bits. Limit the
+ * compare to 8bits, so that we don't miss old keys and delete
+ * them.
+ */
+ if ((entry.vno & 0xff) != (old_kvno & 0xff)) {
+ krb5_error_code rc;
+
+ /* Release the enumeration. We are going to
+ * have to start this from the top again,
+ * because deletes during enumeration may not
+ * always be consistent.
+ *
+ * Also, the enumeration locks a FILE: keytab
+ */
+ krb5_kt_end_seq_get(context, keytab, &cursor);
+
+ code = krb5_kt_remove_entry(context, keytab, &entry);
+ krb5_kt_free_entry(context, &entry);
+
+ /* Make sure we do not double free */
+ ZERO_STRUCT(entry);
+
+ /* Deleted: Restart from the top */
+ rc = krb5_kt_start_seq_get(context, keytab, &cursor);
+ if (rc != 0) {
+ krb5_kt_free_entry(context, &entry);
+
+ /* Make sure we do not double free */
+ ZERO_STRUCT(entry);
+
+ DEBUG(1, ("failed to restart enumeration of keytab: %s\n",
+ smb_get_krb5_error_message(context,
+ code,
+ tmp_ctx)));
+
+ talloc_free(tmp_ctx);
+ return rc;
+ }
+
+ if (code != 0) {
+ break;
+ }
+
+ } else {
+ *found_previous = true;
+ }
+
+ /* Free the entry, we don't need it any more */
+ krb5_kt_free_entry(context, &entry);
+ /* Make sure we do not double free */
+ ZERO_STRUCT(entry);
+ } while (code == 0);
+
+ krb5_kt_end_seq_get(context, keytab, &cursor);
+
+ switch (code) {
+ case 0:
+ break;
+ case ENOENT:
+ case KRB5_KT_END:
+ break;
+ default:
+ *error_string = talloc_asprintf(mem_ctx,
+ "failed in deleting old entries for principal: %s\n",
+ smb_get_krb5_error_message(context,
+ code,
+ mem_ctx));
+ }
+
+ code = 0;
+done:
+ talloc_free(tmp_ctx);
+ return code;
+}
diff --git a/source4/auth/kerberos/krb5_init_context.c b/source4/auth/kerberos/krb5_init_context.c
new file mode 100644
index 0000000..e8114af
--- /dev/null
+++ b/source4/auth/kerberos/krb5_init_context.c
@@ -0,0 +1,885 @@
+/*
+ Unix SMB/CIFS implementation.
+ Wrapper for krb5_init_context
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Stefan Metzmacher 2004
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/kerberos.h"
+#include <tevent.h>
+#include "auth/kerberos/kerberos.h"
+#include "lib/socket/socket.h"
+#include "lib/stream/packet.h"
+#include "system/network.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+#include "../lib/tsocket/tsocket.h"
+#include "krb5_init_context.h"
+#ifdef SAMBA4_USES_HEIMDAL
+#include "../lib/dbwrap/dbwrap.h"
+#include "../lib/dbwrap/dbwrap_rbt.h"
+#include "../lib/util/util_tdb.h"
+#include <krb5/send_to_kdc_plugin.h>
+#endif
+
+/*
+ context structure for operations on cldap packets
+*/
+struct smb_krb5_socket {
+ struct socket_context *sock;
+
+ /* the fd event */
+ struct tevent_fd *fde;
+
+ NTSTATUS status;
+ DATA_BLOB request, reply;
+
+ struct packet_context *packet;
+
+ size_t partial_read;
+#ifdef SAMBA4_USES_HEIMDAL
+ krb5_krbhst_info *hi;
+#endif
+};
+
+static krb5_error_code smb_krb5_context_destroy(struct smb_krb5_context *ctx)
+{
+#ifdef SAMBA4_USES_HEIMDAL
+ if (ctx->pvt_log_data) {
+ /* Otherwise krb5_free_context will try and close what we
+ * have already free()ed */
+ krb5_set_warn_dest(ctx->krb5_context, NULL);
+ krb5_closelog(ctx->krb5_context,
+ (krb5_log_facility *)ctx->pvt_log_data);
+ }
+#endif
+ krb5_free_context(ctx->krb5_context);
+ return 0;
+}
+
+#ifdef SAMBA4_USES_HEIMDAL
+/* We never close down the DEBUG system, and no need to unreference the use */
+static void smb_krb5_debug_close(void *private_data) {
+ return;
+}
+#endif
+
+#ifdef SAMBA4_USES_HEIMDAL
+static void smb_krb5_debug_wrapper(
+#ifdef HAVE_KRB5_ADDLOG_FUNC_NEED_CONTEXT
+ krb5_context ctx,
+#endif /* HAVE_KRB5_ADDLOG_FUNC_NEED_CONTEXT */
+ const char *timestr, const char *msg, void *private_data)
+{
+ DEBUGC(DBGC_KERBEROS, 3, ("Kerberos: %s\n", msg));
+}
+#endif
+
+#ifdef SAMBA4_USES_HEIMDAL
+/*
+ handle recv events on a smb_krb5 socket
+*/
+static void smb_krb5_socket_recv(struct smb_krb5_socket *smb_krb5)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(smb_krb5);
+ DATA_BLOB blob;
+ size_t nread, dsize;
+
+ smb_krb5->status = socket_pending(smb_krb5->sock, &dsize);
+ if (!NT_STATUS_IS_OK(smb_krb5->status)) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ blob = data_blob_talloc(tmp_ctx, NULL, dsize);
+ if (blob.data == NULL && dsize != 0) {
+ smb_krb5->status = NT_STATUS_NO_MEMORY;
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ smb_krb5->status = socket_recv(smb_krb5->sock, blob.data, blob.length, &nread);
+ if (!NT_STATUS_IS_OK(smb_krb5->status)) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+ blob.length = nread;
+
+ if (nread == 0) {
+ smb_krb5->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ DEBUG(4,("Received smb_krb5 packet of length %d\n",
+ (int)blob.length));
+
+ talloc_steal(smb_krb5, blob.data);
+ smb_krb5->reply = blob;
+ talloc_free(tmp_ctx);
+}
+
+static NTSTATUS smb_krb5_full_packet(void *private_data, DATA_BLOB data)
+{
+ struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
+ talloc_steal(smb_krb5, data.data);
+ smb_krb5->reply = data;
+ smb_krb5->reply.length -= 4;
+ smb_krb5->reply.data += 4;
+ return NT_STATUS_OK;
+}
+
+/*
+ handle request timeouts
+*/
+static void smb_krb5_request_timeout(struct tevent_context *event_ctx,
+ struct tevent_timer *te, struct timeval t,
+ void *private_data)
+{
+ struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
+ DEBUG(5,("Timed out smb_krb5 packet\n"));
+ smb_krb5->status = NT_STATUS_IO_TIMEOUT;
+}
+
+static void smb_krb5_error_handler(void *private_data, NTSTATUS status)
+{
+ struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
+ smb_krb5->status = status;
+}
+
+/*
+ handle send events on a smb_krb5 socket
+*/
+static void smb_krb5_socket_send(struct smb_krb5_socket *smb_krb5)
+{
+ NTSTATUS status;
+
+ size_t len;
+
+ len = smb_krb5->request.length;
+ status = socket_send(smb_krb5->sock, &smb_krb5->request, &len);
+
+ if (!NT_STATUS_IS_OK(status)) return;
+
+ TEVENT_FD_READABLE(smb_krb5->fde);
+
+ TEVENT_FD_NOT_WRITEABLE(smb_krb5->fde);
+ return;
+}
+
+
+/*
+ handle fd events on a smb_krb5_socket
+*/
+static void smb_krb5_socket_handler(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
+ switch (smb_krb5->hi->proto) {
+ case KRB5_KRBHST_UDP:
+ if (flags & TEVENT_FD_READ) {
+ smb_krb5_socket_recv(smb_krb5);
+ return;
+ }
+ if (flags & TEVENT_FD_WRITE) {
+ smb_krb5_socket_send(smb_krb5);
+ return;
+ }
+ /* not reached */
+ return;
+ case KRB5_KRBHST_TCP:
+ if (flags & TEVENT_FD_READ) {
+ packet_recv(smb_krb5->packet);
+ return;
+ }
+ if (flags & TEVENT_FD_WRITE) {
+ packet_queue_run(smb_krb5->packet);
+ return;
+ }
+ /* not reached */
+ return;
+ case KRB5_KRBHST_HTTP:
+ /* can't happen */
+ break;
+ }
+}
+
+static krb5_error_code smb_krb5_send_and_recv_func_int(struct smb_krb5_context *smb_krb5_context,
+ struct tevent_context *ev,
+ krb5_krbhst_info *hi,
+ struct addrinfo *ai,
+ smb_krb5_send_to_kdc_func func,
+ void *data,
+ time_t timeout,
+ const krb5_data *send_buf,
+ krb5_data *recv_buf)
+{
+ krb5_error_code ret;
+ NTSTATUS status;
+ const char *name;
+ struct addrinfo *a;
+ struct smb_krb5_socket *smb_krb5;
+
+ DATA_BLOB send_blob;
+
+ TALLOC_CTX *frame = talloc_stackframe();
+ if (frame == NULL) {
+ return ENOMEM;
+ }
+
+ send_blob = data_blob_const(send_buf->data, send_buf->length);
+
+ for (a = ai; a; a = a->ai_next) {
+ struct socket_address *remote_addr;
+ smb_krb5 = talloc(frame, struct smb_krb5_socket);
+ if (!smb_krb5) {
+ TALLOC_FREE(frame);
+ return ENOMEM;
+ }
+ smb_krb5->hi = hi;
+
+ switch (a->ai_family) {
+ case PF_INET:
+ name = "ipv4";
+ break;
+#ifdef HAVE_IPV6
+ case PF_INET6:
+ name = "ipv6";
+ break;
+#endif
+ default:
+ TALLOC_FREE(frame);
+ return EINVAL;
+ }
+
+ status = NT_STATUS_INVALID_PARAMETER;
+ switch (hi->proto) {
+ case KRB5_KRBHST_UDP:
+ status = socket_create(smb_krb5, name,
+ SOCKET_TYPE_DGRAM,
+ &smb_krb5->sock, 0);
+ break;
+ case KRB5_KRBHST_TCP:
+ status = socket_create(smb_krb5, name,
+ SOCKET_TYPE_STREAM,
+ &smb_krb5->sock, 0);
+ break;
+ case KRB5_KRBHST_HTTP:
+ TALLOC_FREE(frame);
+ return EINVAL;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(smb_krb5);
+ continue;
+ }
+
+ remote_addr = socket_address_from_sockaddr(smb_krb5, a->ai_addr, a->ai_addrlen);
+ if (!remote_addr) {
+ talloc_free(smb_krb5);
+ continue;
+ }
+
+ status = socket_connect_ev(smb_krb5->sock, NULL, remote_addr, 0, ev);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(smb_krb5);
+ continue;
+ }
+
+ /* Setup the FDE, start listening for read events
+ * from the start (otherwise we may miss a socket
+ * drop) and mark as AUTOCLOSE along with the fde */
+
+ /* This is equivalent to EVENT_FD_READABLE(smb_krb5->fde) */
+ smb_krb5->fde = tevent_add_fd(ev, smb_krb5->sock,
+ socket_get_fd(smb_krb5->sock),
+ TEVENT_FD_READ,
+ smb_krb5_socket_handler, smb_krb5);
+ /* its now the job of the event layer to close the socket */
+ tevent_fd_set_close_fn(smb_krb5->fde, socket_tevent_fd_close_fn);
+ socket_set_flags(smb_krb5->sock, SOCKET_FLAG_NOCLOSE);
+
+ tevent_add_timer(ev, smb_krb5,
+ timeval_current_ofs(timeout, 0),
+ smb_krb5_request_timeout, smb_krb5);
+
+ smb_krb5->status = NT_STATUS_OK;
+ smb_krb5->reply = data_blob(NULL, 0);
+
+ switch (hi->proto) {
+ case KRB5_KRBHST_UDP:
+ TEVENT_FD_WRITEABLE(smb_krb5->fde);
+ smb_krb5->request = send_blob;
+ break;
+ case KRB5_KRBHST_TCP:
+
+ smb_krb5->packet = packet_init(smb_krb5);
+ if (smb_krb5->packet == NULL) {
+ talloc_free(smb_krb5);
+ return ENOMEM;
+ }
+ packet_set_private(smb_krb5->packet, smb_krb5);
+ packet_set_socket(smb_krb5->packet, smb_krb5->sock);
+ packet_set_callback(smb_krb5->packet, smb_krb5_full_packet);
+ packet_set_full_request(smb_krb5->packet, packet_full_request_u32);
+ packet_set_error_handler(smb_krb5->packet, smb_krb5_error_handler);
+ packet_set_event_context(smb_krb5->packet, ev);
+ packet_set_fde(smb_krb5->packet, smb_krb5->fde);
+
+ smb_krb5->request = data_blob_talloc(smb_krb5, NULL, send_blob.length + 4);
+ RSIVAL(smb_krb5->request.data, 0, send_blob.length);
+ memcpy(smb_krb5->request.data+4, send_blob.data, send_blob.length);
+ packet_send(smb_krb5->packet, smb_krb5->request);
+ break;
+ case KRB5_KRBHST_HTTP:
+ TALLOC_FREE(frame);
+ return EINVAL;
+ }
+ while ((NT_STATUS_IS_OK(smb_krb5->status)) && !smb_krb5->reply.length) {
+ if (tevent_loop_once(ev) != 0) {
+ TALLOC_FREE(frame);
+ return EINVAL;
+ }
+
+ if (func) {
+ /* After each and every event loop, reset the
+ * send_to_kdc pointers to what they were when
+ * we entered this loop. That way, if a
+ * nested event has invalidated them, we put
+ * it back before we return to the heimdal
+ * code */
+ ret = smb_krb5_set_send_to_kdc_func(smb_krb5_context,
+ NULL, /* send_to_realm */
+ func,
+ data);
+ if (ret != 0) {
+ TALLOC_FREE(frame);
+ return ret;
+ }
+ }
+ }
+ if (NT_STATUS_EQUAL(smb_krb5->status, NT_STATUS_IO_TIMEOUT)) {
+ talloc_free(smb_krb5);
+ continue;
+ }
+
+ if (!NT_STATUS_IS_OK(smb_krb5->status)) {
+ struct tsocket_address *addr = socket_address_to_tsocket_address(smb_krb5, remote_addr);
+ const char *addr_string = NULL;
+ if (addr) {
+ addr_string = tsocket_address_inet_addr_string(addr, smb_krb5);
+ } else {
+ addr_string = NULL;
+ }
+ DEBUG(2,("Error reading smb_krb5 reply packet: %s from %s\n", nt_errstr(smb_krb5->status),
+ addr_string));
+ talloc_free(smb_krb5);
+ continue;
+ }
+
+ ret = krb5_data_copy(recv_buf, smb_krb5->reply.data, smb_krb5->reply.length);
+ if (ret) {
+ TALLOC_FREE(frame);
+ return ret;
+ }
+ talloc_free(smb_krb5);
+
+ break;
+ }
+ TALLOC_FREE(frame);
+ if (a) {
+ return 0;
+ }
+ return KRB5_KDC_UNREACH;
+}
+
+krb5_error_code smb_krb5_send_and_recv_func(struct smb_krb5_context *smb_krb5_context,
+ void *data,
+ krb5_krbhst_info *hi,
+ time_t timeout,
+ const krb5_data *send_buf,
+ krb5_data *recv_buf)
+{
+ krb5_error_code ret;
+ struct addrinfo *ai;
+
+ struct tevent_context *ev;
+ TALLOC_CTX *frame = talloc_stackframe();
+ if (frame == NULL) {
+ return ENOMEM;
+ }
+
+ if (data == NULL) {
+ /* If no event context was available, then create one for this loop */
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ TALLOC_FREE(frame);
+ return ENOMEM;
+ }
+ } else {
+ ev = talloc_get_type_abort(data, struct tevent_context);
+ }
+
+ ret = krb5_krbhst_get_addrinfo(smb_krb5_context->krb5_context, hi, &ai);
+ if (ret) {
+ TALLOC_FREE(frame);
+ return ret;
+ }
+
+ ret = smb_krb5_send_and_recv_func_int(smb_krb5_context,
+ ev, hi, ai,
+ smb_krb5_send_and_recv_func,
+ data, timeout, send_buf, recv_buf);
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+krb5_error_code smb_krb5_send_and_recv_func_forced_tcp(struct smb_krb5_context *smb_krb5_context,
+ struct addrinfo *ai,
+ time_t timeout,
+ const krb5_data *send_buf,
+ krb5_data *recv_buf)
+{
+ krb5_error_code k5ret;
+ krb5_krbhst_info hi = {
+ .proto = KRB5_KRBHST_TCP,
+ };
+ struct tevent_context *ev;
+ TALLOC_CTX *frame = talloc_stackframe();
+ if (frame == NULL) {
+ return ENOMEM;
+ }
+
+ /* no event context is passed in, create one for this loop */
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ TALLOC_FREE(frame);
+ return ENOMEM;
+ }
+
+ /* No need to pass in send_and_recv functions, we won't nest on this private event loop */
+ k5ret = smb_krb5_send_and_recv_func_int(smb_krb5_context, ev, &hi, ai, NULL, NULL,
+ timeout, send_buf, recv_buf);
+ TALLOC_FREE(frame);
+ return k5ret;
+}
+
+static struct db_context *smb_krb5_plugin_db;
+
+struct smb_krb5_send_to_kdc_state {
+ intptr_t key_ptr;
+ struct smb_krb5_context *smb_krb5_context;
+ smb_krb5_send_to_realm_func send_to_realm;
+ smb_krb5_send_to_kdc_func send_to_kdc;
+ void *private_data;
+};
+
+static int smb_krb5_send_to_kdc_state_destructor(struct smb_krb5_send_to_kdc_state *state)
+{
+ TDB_DATA key = make_tdb_data((uint8_t *)&state->key_ptr, sizeof(state->key_ptr));
+ struct db_record *rec = NULL;
+ NTSTATUS status;
+
+ rec = dbwrap_fetch_locked(smb_krb5_plugin_db, state, key);
+ if (rec == NULL) {
+ return 0;
+ }
+
+ status = dbwrap_record_delete(rec);
+ TALLOC_FREE(rec);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+
+ state->smb_krb5_context = NULL;
+ return 0;
+}
+
+krb5_error_code smb_krb5_set_send_to_kdc_func(struct smb_krb5_context *smb_krb5_context,
+ smb_krb5_send_to_realm_func send_to_realm,
+ smb_krb5_send_to_kdc_func send_to_kdc,
+ void *private_data)
+{
+ intptr_t key_ptr = (intptr_t)smb_krb5_context->krb5_context;
+ TDB_DATA key = make_tdb_data((uint8_t *)&key_ptr, sizeof(key_ptr));
+ intptr_t value_ptr = (intptr_t)NULL;
+ TDB_DATA value = make_tdb_data(NULL, 0);
+ struct db_record *rec = NULL;
+ struct smb_krb5_send_to_kdc_state *state = NULL;
+ NTSTATUS status;
+
+ rec = dbwrap_fetch_locked(smb_krb5_plugin_db, smb_krb5_context, key);
+ if (rec == NULL) {
+ return ENOMEM;
+ }
+
+ value = dbwrap_record_get_value(rec);
+ if (value.dsize != 0) {
+ SMB_ASSERT(value.dsize == sizeof(value_ptr));
+ memcpy(&value_ptr, value.dptr, sizeof(value_ptr));
+ state = talloc_get_type_abort((const void *)value_ptr,
+ struct smb_krb5_send_to_kdc_state);
+ if (send_to_realm == NULL && send_to_kdc == NULL) {
+ status = dbwrap_record_delete(rec);
+ TALLOC_FREE(rec);
+ if (!NT_STATUS_IS_OK(status)) {
+ return EINVAL;
+ }
+ return 0;
+ }
+ state->send_to_realm = send_to_realm;
+ state->send_to_kdc = send_to_kdc;
+ state->private_data = private_data;
+ TALLOC_FREE(rec);
+ return 0;
+ }
+
+ if (send_to_kdc == NULL && send_to_realm == NULL) {
+ TALLOC_FREE(rec);
+ return 0;
+ }
+
+ state = talloc_zero(smb_krb5_context,
+ struct smb_krb5_send_to_kdc_state);
+ if (state == NULL) {
+ TALLOC_FREE(rec);
+ return ENOMEM;
+ }
+ state->key_ptr = key_ptr;
+ state->smb_krb5_context = smb_krb5_context;
+ state->send_to_realm = send_to_realm;
+ state->send_to_kdc = send_to_kdc;
+ state->private_data = private_data;
+
+ value_ptr = (intptr_t)state;
+ value = make_tdb_data((uint8_t *)&value_ptr, sizeof(value_ptr));
+
+ status = dbwrap_record_store(rec, value, TDB_INSERT);
+ TALLOC_FREE(rec);
+ if (!NT_STATUS_IS_OK(status)) {
+ return EINVAL;
+ }
+ talloc_set_destructor(state, smb_krb5_send_to_kdc_state_destructor);
+
+ return 0;
+}
+
+static krb5_error_code smb_krb5_plugin_init(krb5_context context, void **pctx)
+{
+ *pctx = NULL;
+ return 0;
+}
+
+static void smb_krb5_plugin_fini(void *ctx)
+{
+}
+
+static void smb_krb5_send_to_kdc_state_parser(TDB_DATA key, TDB_DATA value,
+ void *private_data)
+{
+ struct smb_krb5_send_to_kdc_state **state =
+ (struct smb_krb5_send_to_kdc_state **)private_data;
+ intptr_t value_ptr;
+
+ SMB_ASSERT(value.dsize == sizeof(value_ptr));
+ memcpy(&value_ptr, value.dptr, sizeof(value_ptr));
+ *state = talloc_get_type_abort((const void *)value_ptr,
+ struct smb_krb5_send_to_kdc_state);
+}
+
+static struct smb_krb5_send_to_kdc_state *
+smb_krb5_send_to_kdc_get_state(krb5_context context)
+{
+ intptr_t key_ptr = (intptr_t)context;
+ TDB_DATA key = make_tdb_data((uint8_t *)&key_ptr, sizeof(key_ptr));
+ struct smb_krb5_send_to_kdc_state *state = NULL;
+ NTSTATUS status;
+
+ status = dbwrap_parse_record(smb_krb5_plugin_db, key,
+ smb_krb5_send_to_kdc_state_parser,
+ &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NULL;
+ }
+
+ return state;
+}
+
+static krb5_error_code smb_krb5_plugin_send_to_kdc(krb5_context context,
+ void *ctx,
+ krb5_krbhst_info *ho,
+ time_t timeout,
+ const krb5_data *in,
+ krb5_data *out)
+{
+ struct smb_krb5_send_to_kdc_state *state = NULL;
+
+ state = smb_krb5_send_to_kdc_get_state(context);
+ if (state == NULL) {
+ return KRB5_PLUGIN_NO_HANDLE;
+ }
+
+ if (state->send_to_kdc == NULL) {
+ return KRB5_PLUGIN_NO_HANDLE;
+ }
+
+ return state->send_to_kdc(state->smb_krb5_context,
+ state->private_data,
+ ho, timeout, in, out);
+}
+
+static krb5_error_code smb_krb5_plugin_send_to_realm(krb5_context context,
+ void *ctx,
+ krb5_const_realm realm,
+ time_t timeout,
+ const krb5_data *in,
+ krb5_data *out)
+{
+ struct smb_krb5_send_to_kdc_state *state = NULL;
+
+ state = smb_krb5_send_to_kdc_get_state(context);
+ if (state == NULL) {
+ return KRB5_PLUGIN_NO_HANDLE;
+ }
+
+ if (state->send_to_realm == NULL) {
+ return KRB5_PLUGIN_NO_HANDLE;
+ }
+
+ return state->send_to_realm(state->smb_krb5_context,
+ state->private_data,
+ realm, timeout, in, out);
+}
+
+static krb5plugin_send_to_kdc_ftable smb_krb5_plugin_ftable = {
+ KRB5_PLUGIN_SEND_TO_KDC_VERSION_2,
+ smb_krb5_plugin_init,
+ smb_krb5_plugin_fini,
+ smb_krb5_plugin_send_to_kdc,
+ smb_krb5_plugin_send_to_realm
+};
+#endif
+
+krb5_error_code
+smb_krb5_init_context_basic(TALLOC_CTX *tmp_ctx,
+ struct loadparm_context *lp_ctx,
+ krb5_context *_krb5_context)
+{
+ krb5_error_code ret;
+#ifdef SAMBA4_USES_HEIMDAL
+ char **config_files;
+ const char *config_file, *realm;
+#endif
+ krb5_context krb5_ctx;
+
+ ret = smb_krb5_init_context_common(&krb5_ctx);
+ if (ret) {
+ return ret;
+ }
+
+ /* The MIT Kerberos build relies on using the system krb5.conf file.
+ * If you really want to use another file please set KRB5_CONFIG
+ * accordingly. */
+#ifdef SAMBA4_USES_HEIMDAL
+ config_file = lpcfg_config_path(tmp_ctx, lp_ctx, "krb5.conf");
+ if (!config_file) {
+ krb5_free_context(krb5_ctx);
+ return ENOMEM;
+ }
+
+ /* Use our local krb5.conf file by default */
+ ret = krb5_prepend_config_files_default(config_file, &config_files);
+ if (ret) {
+ DEBUG(1,("krb5_prepend_config_files_default failed (%s)\n",
+ smb_get_krb5_error_message(krb5_ctx, ret, tmp_ctx)));
+ krb5_free_context(krb5_ctx);
+ return ret;
+ }
+
+ ret = krb5_set_config_files(krb5_ctx, config_files);
+ krb5_free_config_files(config_files);
+ if (ret) {
+ DEBUG(1,("krb5_set_config_files failed (%s)\n",
+ smb_get_krb5_error_message(krb5_ctx, ret, tmp_ctx)));
+ krb5_free_context(krb5_ctx);
+ return ret;
+ }
+
+ /*
+ * This is already called in smb_krb5_init_context_common(),
+ * but krb5_set_config_files() may resets it.
+ */
+ krb5_set_dns_canonicalize_hostname(krb5_ctx, false);
+
+ realm = lpcfg_realm(lp_ctx);
+ if (realm != NULL) {
+ ret = krb5_set_default_realm(krb5_ctx, realm);
+ if (ret) {
+ DEBUG(1,("krb5_set_default_realm failed (%s)\n",
+ smb_get_krb5_error_message(krb5_ctx, ret, tmp_ctx)));
+ krb5_free_context(krb5_ctx);
+ return ret;
+ }
+ }
+
+ if (smb_krb5_plugin_db == NULL) {
+ /*
+ * while krb5_plugin_register() takes a krb5_context,
+ * plugins are registered into a global list, so
+ * we only do that once
+ *
+ * We maintain a separate dispatch table for per
+ * krb5_context state.
+ */
+ ret = krb5_plugin_register(krb5_ctx, PLUGIN_TYPE_DATA,
+ KRB5_PLUGIN_SEND_TO_KDC,
+ &smb_krb5_plugin_ftable);
+ if (ret) {
+ DEBUG(1,("krb5_plugin_register(KRB5_PLUGIN_SEND_TO_KDC) failed (%s)\n",
+ smb_get_krb5_error_message(krb5_ctx, ret, tmp_ctx)));
+ krb5_free_context(krb5_ctx);
+ return ret;
+ }
+ smb_krb5_plugin_db = db_open_rbt(NULL);
+ if (smb_krb5_plugin_db == NULL) {
+ DEBUG(1,("db_open_rbt() failed\n"));
+ krb5_free_context(krb5_ctx);
+ return ENOMEM;
+ }
+ }
+#endif
+ *_krb5_context = krb5_ctx;
+ return 0;
+}
+
+krb5_error_code smb_krb5_init_context(void *parent_ctx,
+ struct loadparm_context *lp_ctx,
+ struct smb_krb5_context **smb_krb5_context)
+{
+ krb5_error_code ret;
+ TALLOC_CTX *tmp_ctx;
+ krb5_context kctx;
+#ifdef SAMBA4_USES_HEIMDAL
+ krb5_log_facility *logf;
+#endif
+
+ tmp_ctx = talloc_new(parent_ctx);
+ *smb_krb5_context = talloc_zero(tmp_ctx, struct smb_krb5_context);
+
+ if (!*smb_krb5_context || !tmp_ctx) {
+ talloc_free(tmp_ctx);
+ return ENOMEM;
+ }
+
+ ret = smb_krb5_init_context_basic(tmp_ctx, lp_ctx, &kctx);
+ if (ret) {
+ DEBUG(1,("smb_krb5_context_init_basic failed (%s)\n",
+ error_message(ret)));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ (*smb_krb5_context)->krb5_context = kctx;
+
+ talloc_set_destructor(*smb_krb5_context, smb_krb5_context_destroy);
+
+#ifdef SAMBA4_USES_HEIMDAL
+ /* TODO: Should we have a different name here? */
+ ret = krb5_initlog(kctx, "Samba", &logf);
+
+ if (ret) {
+ DEBUG(1,("krb5_initlog failed (%s)\n",
+ smb_get_krb5_error_message(kctx, ret, tmp_ctx)));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ (*smb_krb5_context)->pvt_log_data = logf;
+
+ ret = krb5_addlog_func(kctx, logf, 0 /* min */, -1 /* max */,
+ smb_krb5_debug_wrapper,
+ smb_krb5_debug_close, NULL);
+ if (ret) {
+ DEBUG(1,("krb5_addlog_func failed (%s)\n",
+ smb_get_krb5_error_message(kctx, ret, tmp_ctx)));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ krb5_set_warn_dest(kctx, logf);
+#endif
+ talloc_steal(parent_ctx, *smb_krb5_context);
+ talloc_free(tmp_ctx);
+
+ return 0;
+}
+
+#ifdef SAMBA4_USES_HEIMDAL
+krb5_error_code smb_krb5_context_set_event_ctx(struct smb_krb5_context *smb_krb5_context,
+ struct tevent_context *ev,
+ struct tevent_context **previous_ev)
+{
+ int ret;
+ if (!ev) {
+ return EINVAL;
+ }
+
+ *previous_ev = smb_krb5_context->current_ev;
+
+ smb_krb5_context->current_ev = talloc_reference(smb_krb5_context, ev);
+ if (!smb_krb5_context->current_ev) {
+ return ENOMEM;
+ }
+
+ /* Set use of our socket lib */
+ ret = smb_krb5_set_send_to_kdc_func(smb_krb5_context,
+ NULL, /* send_to_realm */
+ smb_krb5_send_and_recv_func,
+ ev);
+ if (ret) {
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ DEBUG(1,("smb_krb5_set_send_recv_func failed (%s)\n",
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, tmp_ctx)));
+ talloc_free(tmp_ctx);
+ talloc_unlink(smb_krb5_context, smb_krb5_context->current_ev);
+ smb_krb5_context->current_ev = NULL;
+ return ret;
+ }
+ return 0;
+}
+
+krb5_error_code smb_krb5_context_remove_event_ctx(struct smb_krb5_context *smb_krb5_context,
+ struct tevent_context *previous_ev,
+ struct tevent_context *ev)
+{
+ int ret;
+ talloc_unlink(smb_krb5_context, ev);
+ /* If there was a mismatch with things happening on a stack, then don't wipe things */
+ smb_krb5_context->current_ev = previous_ev;
+ /* Set use of our socket lib */
+ ret = smb_krb5_set_send_to_kdc_func(smb_krb5_context,
+ NULL, /* send_to_realm */
+ smb_krb5_send_and_recv_func,
+ previous_ev);
+ if (ret) {
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ DEBUG(1,("smb_krb5_set_send_recv_func failed (%s)\n",
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, tmp_ctx)));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ return 0;
+}
+#endif
diff --git a/source4/auth/kerberos/krb5_init_context.h b/source4/auth/kerberos/krb5_init_context.h
new file mode 100644
index 0000000..c7f7cfd
--- /dev/null
+++ b/source4/auth/kerberos/krb5_init_context.h
@@ -0,0 +1,79 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple kerberos5 routines for active directory
+ Copyright (C) Andrew Bartlett 2005
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _KRB5_INIT_CONTEXT_H_
+#define _KRB5_INIT_CONTEXT_H_
+
+struct smb_krb5_context {
+ krb5_context krb5_context;
+ void *pvt_log_data;
+ struct tevent_context *current_ev;
+};
+
+struct tevent_context;
+struct loadparm_context;
+
+krb5_error_code
+smb_krb5_init_context_basic(TALLOC_CTX *tmp_ctx,
+ struct loadparm_context *lp_ctx,
+ krb5_context *_krb5_context);
+
+krb5_error_code smb_krb5_init_context(void *parent_ctx,
+ struct loadparm_context *lp_ctx,
+ struct smb_krb5_context **smb_krb5_context);
+
+#ifdef SAMBA4_USES_HEIMDAL
+typedef krb5_error_code (*smb_krb5_send_to_realm_func)(struct smb_krb5_context *smb_krb5_context,
+ void *,
+ krb5_const_realm realm,
+ time_t,
+ const krb5_data *,
+ krb5_data *);
+typedef krb5_error_code (*smb_krb5_send_to_kdc_func)(struct smb_krb5_context *smb_krb5_context,
+ void *,
+ krb5_krbhst_info *,
+ time_t,
+ const krb5_data *,
+ krb5_data *);
+
+krb5_error_code smb_krb5_set_send_to_kdc_func(struct smb_krb5_context *smb_krb5_context,
+ smb_krb5_send_to_realm_func send_to_realm,
+ smb_krb5_send_to_kdc_func send_to_kdc,
+ void *private_data);
+
+krb5_error_code smb_krb5_send_and_recv_func(struct smb_krb5_context *smb_krb5_context,
+ void *data,
+ krb5_krbhst_info *hi,
+ time_t timeout,
+ const krb5_data *send_buf,
+ krb5_data *recv_buf);
+krb5_error_code smb_krb5_send_and_recv_func_forced_tcp(struct smb_krb5_context *smb_krb5_context,
+ struct addrinfo *ai,
+ time_t timeout,
+ const krb5_data *send_buf,
+ krb5_data *recv_buf);
+krb5_error_code smb_krb5_context_set_event_ctx(struct smb_krb5_context *smb_krb5_context,
+ struct tevent_context *ev,
+ struct tevent_context **previous_ev);
+krb5_error_code smb_krb5_context_remove_event_ctx(struct smb_krb5_context *smb_krb5_context,
+ struct tevent_context *previous_ev,
+ struct tevent_context *ev);
+#endif
+
+#endif /* _KRB5_INIT_CONTEXT_H_ */
diff --git a/source4/auth/kerberos/srv_keytab.c b/source4/auth/kerberos/srv_keytab.c
new file mode 100644
index 0000000..8eaa150
--- /dev/null
+++ b/source4/auth/kerberos/srv_keytab.c
@@ -0,0 +1,407 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Kerberos utility functions
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file srv_keytab.c
+ *
+ * @brief Kerberos keytab utility functions
+ *
+ */
+
+#include "includes.h"
+#include "system/kerberos.h"
+#include "auth/credentials/credentials.h"
+#include "auth/kerberos/kerberos.h"
+#include "auth/kerberos/kerberos_util.h"
+#include "auth/kerberos/kerberos_srv_keytab.h"
+
+static void keytab_principals_free(krb5_context context,
+ uint32_t num_principals,
+ krb5_principal *set)
+{
+ uint32_t i;
+
+ for (i = 0; i < num_principals; i++) {
+ krb5_free_principal(context, set[i]);
+ }
+}
+
+static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
+ uint32_t num_principals,
+ krb5_principal *principals,
+ krb5_principal salt_princ,
+ int kvno,
+ const char *password_s,
+ krb5_context context,
+ krb5_enctype *enctypes,
+ krb5_keytab keytab,
+ const char **error_string)
+{
+ unsigned int i, p;
+ krb5_error_code ret;
+ krb5_data password;
+ char *unparsed;
+
+ password.data = discard_const_p(char, password_s);
+ password.length = strlen(password_s);
+
+ for (i = 0; enctypes[i]; i++) {
+ krb5_keytab_entry entry;
+
+ ZERO_STRUCT(entry);
+
+ ret = smb_krb5_create_key_from_string(context,
+ salt_princ,
+ NULL,
+ &password,
+ enctypes[i],
+ KRB5_KT_KEY(&entry));
+ if (ret != 0) {
+ *error_string = talloc_strdup(parent_ctx,
+ "Failed to create key from string");
+ return ret;
+ }
+
+ entry.vno = kvno;
+
+ for (p = 0; p < num_principals; p++) {
+ unparsed = NULL;
+ entry.principal = principals[p];
+ ret = krb5_kt_add_entry(context, keytab, &entry);
+ if (ret != 0) {
+ char *k5_error_string =
+ smb_get_krb5_error_message(context,
+ ret, NULL);
+ krb5_unparse_name(context,
+ principals[p], &unparsed);
+ *error_string = talloc_asprintf(parent_ctx,
+ "Failed to add enctype %d entry for "
+ "%s(kvno %d) to keytab: %s\n",
+ (int)enctypes[i], unparsed,
+ kvno, k5_error_string);
+
+ free(unparsed);
+ talloc_free(k5_error_string);
+ krb5_free_keyblock_contents(context,
+ KRB5_KT_KEY(&entry));
+ return ret;
+ }
+
+ DEBUG(5, ("Added key (kvno %d) to keytab (enctype %d)\n",
+ kvno, (int)enctypes[i]));
+ }
+ krb5_free_keyblock_contents(context, KRB5_KT_KEY(&entry));
+ }
+ return 0;
+}
+
+static krb5_error_code create_keytab(TALLOC_CTX *parent_ctx,
+ const char *samAccountName,
+ const char *realm,
+ const char *saltPrincipal,
+ int kvno,
+ const char *new_secret,
+ const char *old_secret,
+ uint32_t supp_enctypes,
+ uint32_t num_principals,
+ krb5_principal *principals,
+ krb5_context context,
+ krb5_keytab keytab,
+ bool add_old,
+ const char **perror_string)
+{
+ krb5_error_code ret;
+ krb5_principal salt_princ = NULL;
+ krb5_enctype *enctypes;
+ TALLOC_CTX *mem_ctx;
+ const char *error_string = NULL;
+
+ if (!new_secret) {
+ /* There is no password here, so nothing to do */
+ return 0;
+ }
+
+ mem_ctx = talloc_new(parent_ctx);
+ if (!mem_ctx) {
+ *perror_string = talloc_strdup(parent_ctx,
+ "unable to allocate tmp_ctx for create_keytab");
+ return ENOMEM;
+ }
+
+ /* The salt used to generate these entries may be different however,
+ * fetch that */
+ ret = krb5_parse_name(context, saltPrincipal, &salt_princ);
+ if (ret) {
+ *perror_string = smb_get_krb5_error_message(context,
+ ret,
+ parent_ctx);
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ ret = ms_suptypes_to_ietf_enctypes(mem_ctx, supp_enctypes, &enctypes);
+ if (ret) {
+ *perror_string = talloc_asprintf(parent_ctx,
+ "create_keytab: generating list of "
+ "encryption types failed (%s)\n",
+ smb_get_krb5_error_message(context,
+ ret, mem_ctx));
+ goto done;
+ }
+
+ ret = keytab_add_keys(mem_ctx,
+ num_principals,
+ principals,
+ salt_princ, kvno, new_secret,
+ context, enctypes, keytab, &error_string);
+ if (ret) {
+ *perror_string = talloc_steal(parent_ctx, error_string);
+ goto done;
+ }
+
+ if (old_secret && add_old && kvno != 0) {
+ ret = keytab_add_keys(mem_ctx,
+ num_principals,
+ principals,
+ salt_princ, kvno - 1, old_secret,
+ context, enctypes, keytab, &error_string);
+ if (ret) {
+ *perror_string = talloc_steal(parent_ctx, error_string);
+ }
+ }
+
+done:
+ krb5_free_principal(context, salt_princ);
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/**
+ * @brief Update a Kerberos keytab and removes any obsolete keytab entries.
+ *
+ * If the keytab does not exist, this function will create one.
+ *
+ * @param[in] parent_ctx Talloc memory context
+ * @param[in] context Kerberos context
+ * @param[in] keytab_name Keytab to open
+ * @param[in] samAccountName User account to update
+ * @param[in] realm Kerberos realm
+ * @param[in] SPNs Service principal names to update
+ * @param[in] num_SPNs Length of SPNs
+ * @param[in] saltPrincipal Salt used for AES encryption.
+ * Required, unless delete_all_kvno is set.
+ * @param[in] old_secret Old password
+ * @param[in] new_secret New password
+ * @param[in] kvno Current key version number
+ * @param[in] supp_enctypes msDS-SupportedEncryptionTypes bit-field
+ * @param[in] delete_all_kvno Removes all obsolete entries, without
+ * recreating the keytab.
+ * @param[out] _keytab If supplied, returns the keytab
+ * @param[out] perror_string Error string on failure
+ *
+ * @return 0 on success, errno on failure
+ */
+krb5_error_code smb_krb5_update_keytab(TALLOC_CTX *parent_ctx,
+ krb5_context context,
+ const char *keytab_name,
+ const char *samAccountName,
+ const char *realm,
+ const char **SPNs,
+ int num_SPNs,
+ const char *saltPrincipal,
+ const char *new_secret,
+ const char *old_secret,
+ int kvno,
+ uint32_t supp_enctypes,
+ bool delete_all_kvno,
+ krb5_keytab *_keytab,
+ const char **perror_string)
+{
+ krb5_keytab keytab = NULL;
+ krb5_error_code ret;
+ bool found_previous = false;
+ TALLOC_CTX *tmp_ctx = NULL;
+ krb5_principal *principals = NULL;
+ uint32_t num_principals = 0;
+ char *upper_realm;
+ const char *error_string = NULL;
+
+ if (keytab_name == NULL) {
+ return ENOENT;
+ }
+
+ ret = krb5_kt_resolve(context, keytab_name, &keytab);
+ if (ret) {
+ *perror_string = smb_get_krb5_error_message(context,
+ ret, parent_ctx);
+ return ret;
+ }
+
+ DEBUG(5, ("Opened keytab %s\n", keytab_name));
+
+ tmp_ctx = talloc_new(parent_ctx);
+ if (!tmp_ctx) {
+ *perror_string = talloc_strdup(parent_ctx,
+ "Failed to allocate memory context");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ upper_realm = strupper_talloc(tmp_ctx, realm);
+ if (upper_realm == NULL) {
+ *perror_string = talloc_strdup(parent_ctx,
+ "Cannot allocate memory to upper case realm");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = smb_krb5_create_principals_array(tmp_ctx,
+ context,
+ samAccountName,
+ upper_realm,
+ num_SPNs,
+ SPNs,
+ &num_principals,
+ &principals,
+ &error_string);
+ if (ret != 0) {
+ *perror_string = talloc_asprintf(parent_ctx,
+ "Failed to load principals from ldb message: %s\n",
+ error_string);
+ goto done;
+ }
+
+ ret = smb_krb5_remove_obsolete_keytab_entries(tmp_ctx,
+ context,
+ keytab,
+ num_principals,
+ principals,
+ kvno,
+ &found_previous,
+ &error_string);
+ if (ret != 0) {
+ *perror_string = talloc_asprintf(parent_ctx,
+ "Failed to remove old principals from keytab: %s\n",
+ error_string);
+ goto done;
+ }
+
+ if (!delete_all_kvno) {
+ /* Create a new keytab. If during the cleanout we found
+ * entries for kvno -1, then don't try and duplicate them.
+ * Otherwise, add kvno, and kvno -1 */
+ if (saltPrincipal == NULL) {
+ *perror_string = talloc_strdup(parent_ctx,
+ "No saltPrincipal provided");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = create_keytab(tmp_ctx,
+ samAccountName, upper_realm, saltPrincipal,
+ kvno, new_secret, old_secret,
+ supp_enctypes,
+ num_principals,
+ principals,
+ context, keytab,
+ found_previous ? false : true,
+ &error_string);
+ if (ret) {
+ *perror_string = talloc_steal(parent_ctx, error_string);
+ }
+ }
+
+ if (ret == 0 && _keytab != NULL) {
+ /* caller wants the keytab handle back */
+ *_keytab = keytab;
+ }
+
+done:
+ keytab_principals_free(context, num_principals, principals);
+ if (ret != 0 || _keytab == NULL) {
+ krb5_kt_close(context, keytab);
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/**
+ * @brief Wrapper around smb_krb5_update_keytab() for creating an in-memory keytab
+ *
+ * @param[in] parent_ctx Talloc memory context
+ * @param[in] context Kerberos context
+ * @param[in] new_secret New password
+ * @param[in] samAccountName User account to update
+ * @param[in] realm Kerberos realm
+ * @param[in] salt_principal Salt used for AES encryption.
+ * Required, unless delete_all_kvno is set.
+ * @param[in] kvno Current key version number
+ * @param[out] keytab If supplied, returns the keytab
+ * @param[out] keytab_name Returns the created keytab name
+ *
+ * @return 0 on success, errno on failure
+ */
+krb5_error_code smb_krb5_create_memory_keytab(TALLOC_CTX *parent_ctx,
+ krb5_context context,
+ const char *new_secret,
+ const char *samAccountName,
+ const char *realm,
+ const char *salt_principal,
+ int kvno,
+ krb5_keytab *keytab,
+ const char **keytab_name)
+{
+ krb5_error_code ret;
+ TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
+ const char *rand_string;
+ const char *error_string = NULL;
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ rand_string = generate_random_str(mem_ctx, 16);
+ if (!rand_string) {
+ talloc_free(mem_ctx);
+ return ENOMEM;
+ }
+
+ *keytab_name = talloc_asprintf(mem_ctx, "MEMORY:%s", rand_string);
+ if (*keytab_name == NULL) {
+ talloc_free(mem_ctx);
+ return ENOMEM;
+ }
+
+ ret = smb_krb5_update_keytab(mem_ctx, context,
+ *keytab_name, samAccountName, realm,
+ NULL, 0, salt_principal, new_secret, NULL,
+ kvno, ENC_ALL_TYPES,
+ false, keytab, &error_string);
+ if (ret == 0) {
+ talloc_steal(parent_ctx, *keytab_name);
+ } else {
+ DEBUG(0, ("Failed to create in-memory keytab: %s\n",
+ error_string));
+ *keytab_name = NULL;
+ }
+ talloc_free(mem_ctx);
+ return ret;
+}
diff --git a/source4/auth/kerberos/wscript_build b/source4/auth/kerberos/wscript_build
new file mode 100644
index 0000000..a26cfaf
--- /dev/null
+++ b/source4/auth/kerberos/wscript_build
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+
+bld.SAMBA_SUBSYSTEM('KRB_INIT_CTX',
+ source='krb5_init_context.c',
+ deps='gssapi krb5samba dbwrap samba-util'
+ )
+
+bld.SAMBA_LIBRARY('authkrb5',
+ source='kerberos_pac.c',
+ autoproto='proto.h',
+ public_deps='ndr-krb5pac krb5samba samba_socket LIBCLI_RESOLVE',
+ deps='common_auth tevent LIBPACKET ndr ldb krb5samba KRB_INIT_CTX KRB5_PAC samba-errors',
+ private_library=True
+ )
+
+bld.SAMBA_SUBSYSTEM('KERBEROS_UTIL',
+ autoproto='kerberos_util.h',
+ source='kerberos_util.c',
+ deps='authkrb5 krb5samba com_err CREDENTIALS_KRB5',
+ )
+
+bld.SAMBA_SUBSYSTEM('KERBEROS_SRV_KEYTAB',
+ autoproto='kerberos_srv_keytab.h',
+ source='srv_keytab.c',
+ deps='authkrb5',
+ )