/*
 * Copyright (c) 2007-2015 Apple Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 * 
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */

#ifndef _NFS_NFS_GSS_H_
#define _NFS_NFS_GSS_H_

#include <gssd/gssd_mach.h>
#include <sys/param.h>
#include <nfs/nfs_ioctl.h>
#include <libkern/crypto/des.h>

#define RPCSEC_GSS			6
#define	RPCSEC_GSS_VERS_1		1

enum rpcsec_gss_proc {
	RPCSEC_GSS_DATA			= 0,
	RPCSEC_GSS_INIT			= 1,
	RPCSEC_GSS_CONTINUE_INIT	= 2,
	RPCSEC_GSS_DESTROY		= 3
};

enum rpcsec_gss_service {
	RPCSEC_GSS_SVC_NONE		= 1,	// sec=krb5
	RPCSEC_GSS_SVC_INTEGRITY	= 2,	// sec=krb5i
	RPCSEC_GSS_SVC_PRIVACY		= 3,	// sec=krb5p
};

/* encoded krb5 OID */
extern u_char krb5_mech[11];

/*
 * GSS-API things
 */
typedef uint32_t OM_uint32;

#define GSS_S_COMPLETE			0
#define GSS_S_CONTINUE_NEEDED		1

/*
 * Some "helper" definitions to make the status code macros obvious.
 * From gssapi.h:
 */
#define GSS_C_CALLING_ERROR_OFFSET 24
#define GSS_C_ROUTINE_ERROR_OFFSET 16
#define GSS_C_SUPPLEMENTARY_OFFSET 0
#define GSS_C_CALLING_ERROR_MASK ((OM_uint32) 0377ul)
#define GSS_C_ROUTINE_ERROR_MASK ((OM_uint32) 0377ul)
#define GSS_C_SUPPLEMENTARY_MASK ((OM_uint32) 0177777ul)

/*
 * The macros that test status codes for error conditions.  Note that the
 * GSS_ERROR() macro has changed slightly from the V1 GSSAPI so that it now
 * evaluates its argument only once.
 */
#define GSS_CALLING_ERROR(x) \
	((x) & (GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET))
#define GSS_ROUTINE_ERROR(x) \
	((x) & (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET))
#define GSS_SUPPLEMENTARY_INFO(x) \
	((x) & (GSS_C_SUPPLEMENTARY_MASK << GSS_C_SUPPLEMENTARY_OFFSET))
#define GSS_ERROR(x) \
	((x) & ((GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET) | \
		(GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET)))

#define GSS_MAXSEQ			0x80000000	// The biggest sequence number
#define GSS_SVC_MAXCONTEXTS		500000		// Max contexts supported
#define GSS_SVC_SEQWINDOW		256		// Server's sequence window
#define GSS_CLNT_SEQLISTMAX		32		// Max length of req seq num list

#define SKEYLEN	8			// length of DES key
#define SKEYLEN3 24			// length of DES3 keyboard
#define MAX_SKEYLEN	SKEYLEN3

#define GSS_MAX_NEG_CACHE_ENTRIES 16
#define GSS_NEG_CACHE_TO 3
#define GSS_PRINT_DELAY	 (8 * 3600)	// Wait day before printing the same error message

typedef struct {
	uint32_t type; 		// See defines below
	uint32_t keybytes; 	// Session key length bytes;
	uint32_t hash_len;
	u_char   skey[MAX_SKEYLEN];	   	// Session key;
	union {
		struct {
			des_cblock  *key;
			des_cbc_key_schedule gss_sched;
			des_cbc_key_schedule gss_sched_Ke;
		} des;
		struct {
			des_cblock		(*key)[3];
			des_cblock		ckey[3];
			des3_cbc_key_schedule	gss_sched;
		} des3;
	} ks_u;
} gss_key_info;

#define NFS_GSS_0DES	0 // Not DES or uninitialized
#define NFS_GSS_1DES	1 // Single DES with DES_MAC_MD5
#define NFS_GSS_3DES	2 // Triple EDE DES KD with SHA1

/*
 * The client's RPCSEC_GSS context information
 */
struct nfs_gss_clnt_ctx {
	lck_mtx_t		*gss_clnt_mtx;
	thread_t		gss_clnt_thread;	// Thread creating context
	TAILQ_ENTRY(nfs_gss_clnt_ctx)	gss_clnt_entries;
	uint32_t		gss_clnt_flags;		// Flag bits - see below
	int32_t			gss_clnt_refcnt;	// Reference count
	kauth_cred_t		gss_clnt_cred;		// Owner of this context
	uint8_t			*gss_clnt_principal;	// Principal to use for this credential
	uint32_t		gss_clnt_prinlen;	// Length of principal
	gssd_nametype		gss_clnt_prinnt;	// Name type of principal
	char			*gss_clnt_display;	// display name of principal
	uint32_t		gss_clnt_proc;		// Current GSS proc for cred
	uint32_t		gss_clnt_seqnum;	// GSS sequence number
	uint32_t		gss_clnt_service;	// Indicates krb5, krb5i or krb5p
	uint8_t			*gss_clnt_handle;	// Identifies server context
	uint32_t		gss_clnt_handle_len;	// Size of server's ctx handle
	time_t			gss_clnt_nctime;	// When context was put in the negative cache
	uint32_t		gss_clnt_seqwin;	// Server's seq num window
	uint32_t		*gss_clnt_seqbits;	// Bitmap to track seq numbers in use
	mach_port_t		gss_clnt_mport;		// Mach port for gssd upcall
	uint8_t			*gss_clnt_verf;		// RPC verifier from server
	uint8_t			*gss_clnt_svcname;	// Service name e.g. "nfs/big.apple.com"
	uint32_t		gss_clnt_svcnamlen;	// Service name length
	gssd_nametype		gss_clnt_svcnt;		// Service name type
	gssd_cred		gss_clnt_cred_handle;	// Opaque cred handle from gssd
	gssd_ctx		gss_clnt_context;	// Opaque context handle from gssd
	uint8_t			*gss_clnt_token;	// GSS token exchanged via gssd & server
	uint32_t		gss_clnt_tokenlen;	// Length of token
	gss_key_info		*gss_clnt_kinfo;		// GSS key info
	uint32_t		gss_clnt_gssd_flags;	// Special flag bits to gssd
	uint32_t		gss_clnt_major;		// GSS major result from gssd or server
	uint32_t		gss_clnt_minor;		// GSS minor result from gssd or server
	time_t			gss_clnt_ptime;		// When last error message was printed
};

/*
 * gss_clnt_flags
 */
#define GSS_CTX_COMPLETE	0x00000001	// Context is complete
#define GSS_CTX_INVAL		0x00000002	// Context is invalid
#define GSS_CTX_STICKY		0x00000004	// Context has been set by user
#define GSS_NEEDSEQ		0x00000008	// Need a sequence number
#define GSS_NEEDCTX		0x00000010	// Need the context
#define GSS_CTX_DESTROY		0x00000020	// Context is being destroyed, don't cache

/*
 * The server's RPCSEC_GSS context information
 */
struct nfs_gss_svc_ctx {
	lck_mtx_t		*gss_svc_mtx;
	LIST_ENTRY(nfs_gss_svc_ctx)	gss_svc_entries;
	uint32_t		gss_svc_handle;		// Identifies server context to client
	uint32_t		gss_svc_refcnt;		// Reference count
	uint32_t		gss_svc_proc;		// Current GSS proc from cred
	uid_t			gss_svc_uid;		// UID of this user
	gid_t			gss_svc_gids[NGROUPS];	// GIDs of this user
	uint32_t		gss_svc_ngroups;	// Count of gids
	uint64_t		gss_svc_incarnation;	// Delete ctx if we exceed this + ttl value
	uint32_t		gss_svc_seqmax;		// Current max GSS sequence number
	uint32_t		gss_svc_seqwin;		// GSS sequence number window
	uint32_t		*gss_svc_seqbits;	// Bitmap to track seq numbers
	gssd_cred		gss_svc_cred_handle;	// Opaque cred handle from gssd
	gssd_ctx		gss_svc_context;	// Opaque context handle from gssd
	u_char			*gss_svc_token;		// GSS token exchanged via gssd & client
	uint32_t		gss_svc_tokenlen;	// Length of token
	gss_key_info		gss_svc_kinfo;		// Session key info
	uint32_t		gss_svc_major;		// GSS major result from gssd
	uint32_t		gss_svc_minor;		// GSS minor result from gssd
};

#define SVC_CTX_HASHSZ	64
#define SVC_CTX_HASH(handle)	((handle) % SVC_CTX_HASHSZ)
LIST_HEAD(nfs_gss_svc_ctx_hashhead, nfs_gss_svc_ctx);

/*
 * Macros to manipulate bits in the sequence window
 */
#define win_getbit(bits, bit)      ((bits[(bit) / 32] &   (1 << (bit) % 32)) != 0)
#define win_setbit(bits, bit)   do { bits[(bit) / 32] |=  (1 << (bit) % 32); } while (0)
#define win_resetbit(bits, bit) do { bits[(bit) / 32] &= ~(1 << (bit) % 32); } while (0)

/*
 * Server context stale times
 */
#define GSS_CTX_PEND		5 		// seconds
#define GSS_CTX_EXPIRE		(8 * 3600)	// seconds
#define GSS_CTX_TTL_MIN		1		// seconds
#define GSS_TIMER_PERIOD	300		// seconds
#define MSECS_PER_SEC		1000

#define auth_is_kerberized(auth) \
	(auth == RPCAUTH_KRB5 || \
	 auth == RPCAUTH_KRB5I || \
	 auth == RPCAUTH_KRB5P)

__BEGIN_DECLS

void	nfs_gss_init(void);
uid_t	nfs_cred_getasid2uid(kauth_cred_t);
int	nfs_gss_clnt_cred_put(struct nfsreq *, struct nfsm_chain *, mbuf_t);
int	nfs_gss_clnt_verf_get(struct nfsreq *, struct nfsm_chain *,
		uint32_t, uint32_t, uint32_t *);
void	nfs_gss_clnt_rpcdone(struct nfsreq *);
int	nfs_gss_clnt_args_restore(struct nfsreq *);
int	nfs_gss_clnt_ctx_renew(struct nfsreq *);
void	nfs_gss_clnt_ctx_ref(struct nfsreq *, struct nfs_gss_clnt_ctx *);
void	nfs_gss_clnt_ctx_unref(struct nfsreq *);
void	nfs_gss_clnt_ctx_unmount(struct nfsmount *);
int	nfs_gss_clnt_ctx_remove(struct nfsmount *, kauth_cred_t);
int	nfs_gss_clnt_ctx_set_principal(struct nfsmount *, vfs_context_t, uint8_t *, uint32_t, uint32_t);
int	nfs_gss_clnt_ctx_get_principal(struct nfsmount *, vfs_context_t, struct user_nfs_gss_principal *);
int	nfs_gss_svc_cred_get(struct nfsrv_descript *, struct nfsm_chain *);
int	nfs_gss_svc_verf_put(struct nfsrv_descript *, struct nfsm_chain *);
int	nfs_gss_svc_ctx_init(struct nfsrv_descript *, struct nfsrv_sock *, mbuf_t *);
int	nfs_gss_svc_prepare_reply(struct nfsrv_descript *, struct nfsm_chain *);
int	nfs_gss_svc_protect_reply(struct nfsrv_descript *, mbuf_t);
void	nfs_gss_svc_ctx_deref(struct nfs_gss_svc_ctx *);
void	nfs_gss_svc_cleanup(void);

__END_DECLS
#endif /* _NFS_NFS_GSS_H_ */
