git: 837b13af68bd - main - Revert "Remove Secure RPC DES authentication"

From: Lexi Winter <ivy_at_FreeBSD.org>
Date: Fri, 15 Aug 2025 16:59:31 UTC
The branch main has been updated by ivy:

URL: https://cgit.FreeBSD.org/src/commit/?id=837b13af68bde317414f6c0ce295df66308cc31b

commit 837b13af68bde317414f6c0ce295df66308cc31b
Author:     Lexi Winter <ivy@FreeBSD.org>
AuthorDate: 2025-08-15 15:56:46 +0000
Commit:     Lexi Winter <ivy@FreeBSD.org>
CommitDate: 2025-08-15 16:59:18 +0000

    Revert "Remove Secure RPC DES authentication"
    
    This reverts commit 7ac276298b72982189ac1a5b17461936dc00163e.
    
    Requested by:   kib
---
 ObsoleteFiles.inc                         |   4 -
 RELNOTES                                  |   6 -
 UPDATING                                  |   4 +
 include/rpc/auth_des.h                    |  79 ++++-
 lib/libc/rpc/Symbol.map                   |  19 ++
 lib/libc/rpc/auth_des.c                   | 455 ++++++++++++++++++++++++++++-
 lib/libc/rpc/authdes_prot.c               |  44 ++-
 lib/libc/rpc/key_call.c                   | 424 ++++++++++++++++++++++++---
 lib/libc/rpc/publickey.5                  |  40 +++
 lib/libc/rpc/rpc_secure.3                 | 177 +++++++++++-
 lib/libc/rpc/rpc_soc.3                    |  13 +-
 lib/libc/rpc/rpc_soc.c                    |  31 +-
 lib/libc/rpc/svc_auth.c                   |   8 +
 lib/libc/rpc/svc_auth_des.c               | 460 +++++++++++++++++++++++++++++-
 lib/librpcsvc/Makefile                    |   2 +-
 lib/librpcsvc/yp_update.c                 | 199 +++++++++++++
 libexec/rc/rc.conf                        |   1 +
 libexec/rc/rc.d/Makefile                  |   1 +
 libexec/rc/rc.d/ypupdated                 |  35 +++
 share/man/man5/rc.conf.5                  |   9 +-
 sys/rpc/auth.h                            |  26 ++
 tools/build/mk/OptionalObsoleteFiles.inc  |   2 +
 usr.sbin/Makefile                         |   1 +
 usr.sbin/rpc.ypupdated/Makefile           |  32 +++
 usr.sbin/rpc.ypupdated/Makefile.depend    |  18 ++
 usr.sbin/rpc.ypupdated/update.c           | 328 +++++++++++++++++++++
 usr.sbin/rpc.ypupdated/yp_dbdelete.c      |  68 +++++
 usr.sbin/rpc.ypupdated/yp_dbupdate.c      | 147 ++++++++++
 usr.sbin/rpc.ypupdated/ypupdate           |  32 +++
 usr.sbin/rpc.ypupdated/ypupdated_extern.h |  32 +++
 usr.sbin/rpc.ypupdated/ypupdated_main.c   | 287 +++++++++++++++++++
 usr.sbin/rpc.ypupdated/ypupdated_server.c | 227 +++++++++++++++
 32 files changed, 3122 insertions(+), 89 deletions(-)

diff --git a/ObsoleteFiles.inc b/ObsoleteFiles.inc
index 4db0704d88ef..aaec7ace84fc 100644
--- a/ObsoleteFiles.inc
+++ b/ObsoleteFiles.inc
@@ -54,10 +54,6 @@
 # 20250812: Remove a bogus manlink
 OLD_FILES+=usr/share/man/man3/quota_statfs.3.gz
 
-# 20250810: Removal of remaining Secure RPC (DES) bits
-OLD_FILES+=usr/sbin/rpc.ypupdated
-OLD_FILES+=etc/rc.d/ypupdated
-
 # 20250808: nvmfd removed from base install
 OLD_FILES+=usr/sbin/nvmfd
 OLD_FILES+=usr/share/man/man8/nvmfd.8.gz
diff --git a/RELNOTES b/RELNOTES
index c11e8543746c..040c33b7b89c 100644
--- a/RELNOTES
+++ b/RELNOTES
@@ -16,12 +16,6 @@ cce64f2e6851:
 	This only works for exported ZFS file systems that have
 	block cloning enabled, at this time.
 
-7ac276298b72, 7b8c9de17448, 1271b1d747a7, 9dcb984251b3:
-	Support for Secure RPC DES authentication has been removed.  This
-	includes publickey(5), keyserv(8) and the rpc_secure(3) routines which
-	rely on keyserv.  The libc symbols are still present for backward
-	compatibility, but all functions will unconditionally return an error.
-
 37b2cb5ecb0f:
 	Add support to VOP_COPY_FILE_RANGE() for block cloning.
 	At this time, ZFS is the only local file system that supports
diff --git a/UPDATING b/UPDATING
index 82399310d299..587ad2f93cf2 100644
--- a/UPDATING
+++ b/UPDATING
@@ -27,6 +27,10 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 15.x IS SLOW:
 	world, or to merely disable the most expensive debugging functionality
 	at runtime, run "ln -s 'abort:false,junk:false' /etc/malloc.conf".)
 
+20250815:
+	The removal of Secure RPC DES authentication notced in 20250810
+	has been reverted.  (However, it is still non-functional.)
+
 20250813:
 	Commit cce64f2e6851 changed the internal KAPI between the NFS
 	modules.  As such, all of them need to be rebuilt from sources.
diff --git a/include/rpc/auth_des.h b/include/rpc/auth_des.h
index 1b4943a74b8b..0ff43c13139b 100644
--- a/include/rpc/auth_des.h
+++ b/include/rpc/auth_des.h
@@ -33,14 +33,91 @@
  * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc.
  */
 
-/* Note, RPC DES authentication was removed in FreeBSD 15.0. */
+/*
+ * auth_des.h, Protocol for DES style authentication for RPC
+ */
 
 #ifndef _AUTH_DES_
 #define _AUTH_DES_
 
+/*
+ * There are two kinds of "names": fullnames and nicknames
+ */
+enum authdes_namekind {
+	ADN_FULLNAME, 
+	ADN_NICKNAME
+};
+
+/*
+ * A fullname contains the network name of the client, 
+ * a conversation key and the window
+ */
+struct authdes_fullname {
+	char *name;		/* network name of client, up to MAXNETNAMELEN */
+	des_block key;		/* conversation key */
+	u_long window;		/* associated window */
+};
+
+
+/*
+ * A credential 
+ */
+struct authdes_cred {
+	enum authdes_namekind adc_namekind;
+	struct authdes_fullname adc_fullname;
+	u_long adc_nickname;
+};
+
+
+
+/*
+ * A des authentication verifier 
+ */
+struct authdes_verf {
+	union {
+		struct timeval adv_ctime;	/* clear time */
+		des_block adv_xtime;		/* crypt time */
+	} adv_time_u;
+	u_long adv_int_u;
+};
+
+/*
+ * des authentication verifier: client variety
+ *
+ * adv_timestamp is the current time.
+ * adv_winverf is the credential window + 1.
+ * Both are encrypted using the conversation key.
+ */
+#define adv_timestamp	adv_time_u.adv_ctime
+#define adv_xtimestamp	adv_time_u.adv_xtime
+#define adv_winverf	adv_int_u
+
+/*
+ * des authentication verifier: server variety
+ *
+ * adv_timeverf is the client's timestamp + client's window
+ * adv_nickname is the server's nickname for the client.
+ * adv_timeverf is encrypted using the conversation key.
+ */
+#define adv_timeverf	adv_time_u.adv_ctime
+#define adv_xtimeverf	adv_time_u.adv_xtime
+#define adv_nickname	adv_int_u
+
+/*
+ * Map a des credential into a unix cred.
+ *
+ */
+__BEGIN_DECLS
+extern int authdes_getucred( struct authdes_cred *, uid_t *, gid_t *, int *, gid_t * );
+__END_DECLS
+
 __BEGIN_DECLS
+extern bool_t	xdr_authdes_cred(XDR *, struct authdes_cred *);
+extern bool_t	xdr_authdes_verf(XDR *, struct authdes_verf *);
 extern int	rtime(dev_t, struct netbuf *, int, struct timeval *,
 		    struct timeval *);
+extern void	kgetnetname(char *);
+extern enum auth_stat _svcauth_des(struct svc_req *, struct rpc_msg *);
 __END_DECLS
 
 #endif /* ndef _AUTH_DES_ */
diff --git a/lib/libc/rpc/Symbol.map b/lib/libc/rpc/Symbol.map
index 61e8e084b1e0..105d6fb6b54e 100644
--- a/lib/libc/rpc/Symbol.map
+++ b/lib/libc/rpc/Symbol.map
@@ -8,9 +8,13 @@ FBSD_1.0 {
 	xdr_desargs;
 	xdr_desresp;
 
+	authdes_seccreate;
+	authdes_pk_seccreate;
 	authnone_create;
 	authunix_create;
 	authunix_create_default;
+	xdr_authdes_cred;
+	xdr_authdes_verf;
 	xdr_authunix_parms;
 	bindresvport;
 	bindresvport_sa;
@@ -54,6 +58,15 @@ FBSD_1.0 {
 	endrpcent;
 	getrpcent;
 	getrpcport;
+	key_setsecret;
+	key_secretkey_is_set;
+	key_encryptsession_pk;
+	key_decryptsession_pk;
+	key_encryptsession;
+	key_decryptsession;
+	key_gendes;
+	key_setnet;
+	key_get_conv;
 	xdr_keystatus;
 	xdr_keybuf;
 	xdr_netnamestr;
@@ -117,6 +130,7 @@ FBSD_1.0 {
 	callrpc;
 	registerrpc;
 	clnt_broadcast;
+	authdes_create;
 	clntunix_create;
 	svcunix_create;
 	svcunixfd_create;
@@ -166,6 +180,8 @@ FBSD_1.0 {
 	_authenticate;
 	_svcauth_null;
 	svc_auth_reg;
+	_svcauth_des;
+	authdes_getucred;
 	_svcauth_unix;
 	_svcauth_short;
 	svc_dg_create;
@@ -189,6 +205,9 @@ FBSD_1.8 {
 
 FBSDprivate_1.0 {
 	__des_crypt_LOCAL;
+	__key_encryptsession_pk_LOCAL;
+	__key_decryptsession_pk_LOCAL;
+	__key_gendes_LOCAL;
 	__svc_clean_idle;
 	__rpc_gss_unwrap;
 	__rpc_gss_unwrap_stub;
diff --git a/lib/libc/rpc/auth_des.c b/lib/libc/rpc/auth_des.c
index 754d55cbed3e..c9b20de25cda 100644
--- a/lib/libc/rpc/auth_des.c
+++ b/lib/libc/rpc/auth_des.c
@@ -30,34 +30,463 @@
 /*
  * Copyright (c) 1988 by Sun Microsystems, Inc.
  */
-
 /*
- * Secure RPC DES authentication was removed in FreeBSD 15.0.
- * These symbols are provided for backward compatibility, but provide no
- * functionality and will always return an error.
+ * auth_des.c, client-side implementation of DES authentication
  */
 
 #include "namespace.h"
 #include "reentrant.h"
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <rpc/des_crypt.h>
+#include <syslog.h>
 #include <rpc/types.h>
 #include <rpc/auth.h>
 #include <rpc/auth_des.h>
+#include <rpc/clnt.h>
+#include <rpc/xdr.h>
+#include <sys/socket.h>
+#undef NIS
 #include <rpcsvc/nis.h>
 #include "un-namespace.h"
+#include "mt_misc.h"
+
+#define USEC_PER_SEC		1000000
+#define RTIME_TIMEOUT		5	/* seconds to wait for sync */
+
+#define AUTH_PRIVATE(auth)	(struct ad_private *) auth->ah_private
+#define ALLOC(object_type)	(object_type *) mem_alloc(sizeof(object_type))
+#define FREE(ptr, size)		mem_free((char *)(ptr), (int) size)
+#define ATTEMPT(xdr_op)		if (!(xdr_op)) return (FALSE)
+
+extern bool_t xdr_authdes_cred( XDR *, struct authdes_cred *);
+extern bool_t xdr_authdes_verf( XDR *, struct authdes_verf *);
+extern int key_encryptsession_pk(char *, netobj *, des_block *);
+
+extern bool_t __rpc_get_time_offset(struct timeval *, nis_server *, char *,
+	char **, char **);
 
-static AUTH *
-__authdes_seccreate(const char *servername, const u_int win,
+/* 
+ * DES authenticator operations vector
+ */
+static void	authdes_nextverf(AUTH *);
+static bool_t	authdes_marshal(AUTH *, XDR *);
+static bool_t	authdes_validate(AUTH *, struct opaque_auth *);
+static bool_t	authdes_refresh(AUTH *, void *);
+static void	authdes_destroy(AUTH *);
+
+static struct auth_ops *authdes_ops(void);
+
+/*
+ * This struct is pointed to by the ah_private field of an "AUTH *"
+ */
+struct ad_private {
+	char *ad_fullname; 		/* client's full name */
+	u_int ad_fullnamelen;		/* length of name, rounded up */
+	char *ad_servername; 		/* server's full name */
+	u_int ad_servernamelen;		/* length of name, rounded up */
+	u_int ad_window;	  	/* client specified window */
+	bool_t ad_dosync;		/* synchronize? */		
+	struct netbuf ad_syncaddr;	/* remote host to synch with */
+	char *ad_timehost;		/* remote host to synch with */
+	struct timeval ad_timediff;	/* server's time - client's time */
+	u_int ad_nickname;		/* server's nickname for client */
+	struct authdes_cred ad_cred;	/* storage for credential */
+	struct authdes_verf ad_verf;	/* storage for verifier */
+	struct timeval ad_timestamp;	/* timestamp sent */
+	des_block ad_xkey;		/* encrypted conversation key */
+	u_char ad_pkey[1024];		/* Server's actual public key */
+	char *ad_netid;			/* Timehost netid */
+	char *ad_uaddr;			/* Timehost uaddr */
+	nis_server *ad_nis_srvr;	/* NIS+ server struct */
+};
+
+AUTH *authdes_pk_seccreate(const char *, netobj *, u_int, const char *,
+	const des_block *, nis_server *);
+	
+/*
+ * documented version of authdes_seccreate
+ */
+/*
+	servername:	network name of server
+	win:		time to live
+	timehost:	optional hostname to sync with
+	ckey:		optional conversation key to use
+*/
+
+AUTH *
+authdes_seccreate(const char *servername, const u_int win,
 	const char *timehost, const des_block *ckey)
 {
-	return (NULL);
+	u_char  pkey_data[1024];
+	netobj  pkey;
+	AUTH    *dummy;
+
+	if (! getpublickey(servername, (char *) pkey_data)) {
+		syslog(LOG_ERR,
+		    "authdes_seccreate: no public key found for %s",
+		    servername);
+		return (NULL);
+	}
+
+	pkey.n_bytes = (char *) pkey_data;
+	pkey.n_len = (u_int)strlen((char *)pkey_data) + 1;
+	dummy = authdes_pk_seccreate(servername, &pkey, win, timehost,
+	    ckey, NULL);
+	return (dummy);
 }
-__sym_compat(authdes_seccreate, __authdes_seccreate, FBSD_1.0);
 
-static AUTH *
-__authdes_pk_seccreate(const char *servername __unused, netobj *pkey __unused,
-	u_int window __unused, const char *timehost __unused,
-	const des_block *ckey __unused, nis_server *srvr __unused)
+/*
+ * Slightly modified version of authdessec_create which takes the public key
+ * of the server principal as an argument. This spares us a call to
+ * getpublickey() which in the nameserver context can cause a deadlock.
+ */
+AUTH *
+authdes_pk_seccreate(const char *servername, netobj *pkey, u_int window,
+	const char *timehost, const des_block *ckey, nis_server *srvr)
 {
+	AUTH *auth;
+	struct ad_private *ad;
+	char namebuf[MAXNETNAMELEN+1];
+
+	/*
+	 * Allocate everything now
+	 */
+	auth = ALLOC(AUTH);
+	if (auth == NULL) {
+		syslog(LOG_ERR, "authdes_pk_seccreate: out of memory");
+		return (NULL);
+	}
+	ad = ALLOC(struct ad_private);
+	if (ad == NULL) {
+		syslog(LOG_ERR, "authdes_pk_seccreate: out of memory");
+		goto failed;
+	}
+	ad->ad_fullname = ad->ad_servername = NULL; /* Sanity reasons */
+	ad->ad_timehost = NULL;
+	ad->ad_netid = NULL;
+	ad->ad_uaddr = NULL;
+	ad->ad_nis_srvr = NULL;
+	ad->ad_timediff.tv_sec = 0;
+	ad->ad_timediff.tv_usec = 0;
+	memcpy(ad->ad_pkey, pkey->n_bytes, pkey->n_len);
+	if (!getnetname(namebuf))
+		goto failed;
+	ad->ad_fullnamelen = RNDUP((u_int) strlen(namebuf));
+	ad->ad_fullname = (char *)mem_alloc(ad->ad_fullnamelen + 1);
+	ad->ad_servernamelen = strlen(servername);
+	ad->ad_servername = (char *)mem_alloc(ad->ad_servernamelen + 1);
+
+	if (ad->ad_fullname == NULL || ad->ad_servername == NULL) {
+		syslog(LOG_ERR, "authdes_seccreate: out of memory");
+		goto failed;
+	}
+	if (timehost != NULL) {
+		ad->ad_timehost = (char *)mem_alloc(strlen(timehost) + 1);
+		if (ad->ad_timehost == NULL) {
+			syslog(LOG_ERR, "authdes_seccreate: out of memory");
+			goto failed;
+		}
+		memcpy(ad->ad_timehost, timehost, strlen(timehost) + 1);
+		ad->ad_dosync = TRUE;
+	} else if (srvr != NULL) {
+		ad->ad_nis_srvr = srvr;	/* transient */
+		ad->ad_dosync = TRUE;
+	} else {
+		ad->ad_dosync = FALSE;
+	}
+	memcpy(ad->ad_fullname, namebuf, ad->ad_fullnamelen + 1);
+	memcpy(ad->ad_servername, servername, ad->ad_servernamelen + 1);
+	ad->ad_window = window;
+	if (ckey == NULL) {
+		if (key_gendes(&auth->ah_key) < 0) {
+			syslog(LOG_ERR,
+	    "authdes_seccreate: keyserv(1m) is unable to generate session key");
+			goto failed;
+		}
+	} else {
+		auth->ah_key = *ckey;
+	}
+
+	/*
+	 * Set up auth handle
+	 */
+	auth->ah_cred.oa_flavor = AUTH_DES;
+	auth->ah_verf.oa_flavor = AUTH_DES;
+	auth->ah_ops = authdes_ops();
+	auth->ah_private = (caddr_t)ad;
+
+	if (!authdes_refresh(auth, NULL)) {
+		goto failed;
+	}
+	ad->ad_nis_srvr = NULL; /* not needed any longer */
+	return (auth);
+
+failed:
+	if (auth)
+		FREE(auth, sizeof (AUTH));
+	if (ad) {
+		if (ad->ad_fullname)
+			FREE(ad->ad_fullname, ad->ad_fullnamelen + 1);
+		if (ad->ad_servername)
+			FREE(ad->ad_servername, ad->ad_servernamelen + 1);
+		if (ad->ad_timehost)
+			FREE(ad->ad_timehost, strlen(ad->ad_timehost) + 1);
+		if (ad->ad_netid)
+			FREE(ad->ad_netid, strlen(ad->ad_netid) + 1);
+		if (ad->ad_uaddr)
+			FREE(ad->ad_uaddr, strlen(ad->ad_uaddr) + 1);
+		FREE(ad, sizeof (struct ad_private));
+	}
 	return (NULL);
 }
-__sym_compat(authdes_pk_seccreate, __authdes_pk_seccreate, FBSD_1.0);
+
+/*
+ * Implement the five authentication operations
+ */
+
+
+/*
+ * 1. Next Verifier
+ */	
+/*ARGSUSED*/
+static void
+authdes_nextverf(AUTH *auth __unused)
+{
+	/* what the heck am I supposed to do??? */
+}
+
+
+/*
+ * 2. Marshal
+ */
+static bool_t
+authdes_marshal(AUTH *auth, XDR *xdrs)
+{
+/* LINTED pointer alignment */
+	struct ad_private *ad = AUTH_PRIVATE(auth);
+	struct authdes_cred *cred = &ad->ad_cred;
+	struct authdes_verf *verf = &ad->ad_verf;
+	des_block cryptbuf[2];	
+	des_block ivec;
+	int status;
+	int len;
+	rpc_inline_t *ixdr;
+
+	/*
+	 * Figure out the "time", accounting for any time difference
+	 * with the server if necessary.
+	 */
+	(void)gettimeofday(&ad->ad_timestamp, NULL);
+	ad->ad_timestamp.tv_sec += ad->ad_timediff.tv_sec;
+	ad->ad_timestamp.tv_usec += ad->ad_timediff.tv_usec;
+	while (ad->ad_timestamp.tv_usec >= USEC_PER_SEC) {
+		ad->ad_timestamp.tv_usec -= USEC_PER_SEC;
+		ad->ad_timestamp.tv_sec++;
+	}
+
+	/*
+	 * XDR the timestamp and possibly some other things, then
+	 * encrypt them.
+	 */
+	ixdr = (rpc_inline_t *)cryptbuf;
+	IXDR_PUT_INT32(ixdr, ad->ad_timestamp.tv_sec);
+	IXDR_PUT_INT32(ixdr, ad->ad_timestamp.tv_usec);
+	if (ad->ad_cred.adc_namekind == ADN_FULLNAME) {
+		IXDR_PUT_U_INT32(ixdr, ad->ad_window);
+		IXDR_PUT_U_INT32(ixdr, ad->ad_window - 1);
+		ivec.key.high = ivec.key.low = 0;	
+		status = cbc_crypt((char *)&auth->ah_key, (char *)cryptbuf, 
+			(u_int) 2 * sizeof (des_block),
+			DES_ENCRYPT | DES_HW, (char *)&ivec);
+	} else {
+		status = ecb_crypt((char *)&auth->ah_key, (char *)cryptbuf, 
+			(u_int) sizeof (des_block),
+			DES_ENCRYPT | DES_HW);
+	}
+	if (DES_FAILED(status)) {
+		syslog(LOG_ERR, "authdes_marshal: DES encryption failure");
+		return (FALSE);
+	}
+	ad->ad_verf.adv_xtimestamp = cryptbuf[0];
+	if (ad->ad_cred.adc_namekind == ADN_FULLNAME) {
+		ad->ad_cred.adc_fullname.window = cryptbuf[1].key.high;
+		ad->ad_verf.adv_winverf = cryptbuf[1].key.low;
+	} else {
+		ad->ad_cred.adc_nickname = ad->ad_nickname;
+		ad->ad_verf.adv_winverf = 0;
+	}
+
+	/*
+	 * Serialize the credential and verifier into opaque
+	 * authentication data.
+	 */
+	if (ad->ad_cred.adc_namekind == ADN_FULLNAME) {
+		len = ((1 + 1 + 2 + 1)*BYTES_PER_XDR_UNIT + ad->ad_fullnamelen);
+	} else {
+		len = (1 + 1)*BYTES_PER_XDR_UNIT;
+	}
+
+	if ((ixdr = xdr_inline(xdrs, 2*BYTES_PER_XDR_UNIT))) {
+		IXDR_PUT_INT32(ixdr, AUTH_DES);
+		IXDR_PUT_INT32(ixdr, len);
+	} else {
+		ATTEMPT(xdr_putint32(xdrs, (int *)&auth->ah_cred.oa_flavor));
+		ATTEMPT(xdr_putint32(xdrs, &len));
+	}
+	ATTEMPT(xdr_authdes_cred(xdrs, cred));
+
+	len = (2 + 1)*BYTES_PER_XDR_UNIT; 
+	if ((ixdr = xdr_inline(xdrs, 2*BYTES_PER_XDR_UNIT))) {
+		IXDR_PUT_INT32(ixdr, AUTH_DES);
+		IXDR_PUT_INT32(ixdr, len);
+	} else {
+		ATTEMPT(xdr_putint32(xdrs, (int *)&auth->ah_verf.oa_flavor));
+		ATTEMPT(xdr_putint32(xdrs, &len));
+	}
+	ATTEMPT(xdr_authdes_verf(xdrs, verf));
+	return (TRUE);
+}
+
+
+/*
+ * 3. Validate
+ */
+static bool_t
+authdes_validate(AUTH *auth, struct opaque_auth *rverf)
+{
+/* LINTED pointer alignment */
+	struct ad_private *ad = AUTH_PRIVATE(auth);
+	struct authdes_verf verf;
+	int status;
+	uint32_t *ixdr;
+	des_block buf;
+
+	if (rverf->oa_length != (2 + 1) * BYTES_PER_XDR_UNIT) {
+		return (FALSE);
+	}
+/* LINTED pointer alignment */
+	ixdr = (uint32_t *)rverf->oa_base;
+	buf.key.high = (uint32_t)*ixdr++;
+	buf.key.low = (uint32_t)*ixdr++;
+	verf.adv_int_u = (uint32_t)*ixdr++;
+
+	/*
+	 * Decrypt the timestamp
+	 */
+	status = ecb_crypt((char *)&auth->ah_key, (char *)&buf,
+		(u_int)sizeof (des_block), DES_DECRYPT | DES_HW);
+
+	if (DES_FAILED(status)) {
+		syslog(LOG_ERR, "authdes_validate: DES decryption failure");
+		return (FALSE);
+	}
+
+	/*
+	 * xdr the decrypted timestamp
+	 */
+/* LINTED pointer alignment */
+	ixdr = (uint32_t *)buf.c;
+	verf.adv_timestamp.tv_sec = IXDR_GET_INT32(ixdr) + 1;
+	verf.adv_timestamp.tv_usec = IXDR_GET_INT32(ixdr);
+
+	/*
+	 * validate
+	 */
+	if (bcmp((char *)&ad->ad_timestamp, (char *)&verf.adv_timestamp,
+		 sizeof(struct timeval)) != 0) {
+		syslog(LOG_DEBUG, "authdes_validate: verifier mismatch");
+		return (FALSE);
+	}
+
+	/*
+	 * We have a nickname now, let's use it
+	 */
+	ad->ad_nickname = verf.adv_nickname;
+	ad->ad_cred.adc_namekind = ADN_NICKNAME;
+	return (TRUE);
+}
+
+/*
+ * 4. Refresh
+ */
+/*ARGSUSED*/
+static bool_t
+authdes_refresh(AUTH *auth, void *dummy __unused)
+{
+/* LINTED pointer alignment */
+	struct ad_private *ad = AUTH_PRIVATE(auth);
+	struct authdes_cred *cred = &ad->ad_cred;
+	int		ok;
+	netobj		pkey;
+
+	if (ad->ad_dosync) {
+                ok = __rpc_get_time_offset(&ad->ad_timediff, ad->ad_nis_srvr,
+		    ad->ad_timehost, &(ad->ad_uaddr),
+		    &(ad->ad_netid));
+		if (! ok) {
+			/*
+			 * Hope the clocks are synced!
+			 */
+			ad->ad_dosync = 0;
+			syslog(LOG_DEBUG,
+			    "authdes_refresh: unable to synchronize clock");
+		 }
+	}
+	ad->ad_xkey = auth->ah_key;
+	pkey.n_bytes = (char *)(ad->ad_pkey);
+	pkey.n_len = (u_int)strlen((char *)ad->ad_pkey) + 1;
+	if (key_encryptsession_pk(ad->ad_servername, &pkey, &ad->ad_xkey) < 0) {
+		syslog(LOG_INFO,
+		    "authdes_refresh: keyserv(1m) is unable to encrypt session key");
+		return (FALSE);
+	}
+	cred->adc_fullname.key = ad->ad_xkey;
+	cred->adc_namekind = ADN_FULLNAME;
+	cred->adc_fullname.name = ad->ad_fullname;
+	return (TRUE);
+}
+
+
+/*
+ * 5. Destroy
+ */
+static void
+authdes_destroy(AUTH *auth)
+{
+/* LINTED pointer alignment */
+	struct ad_private *ad = AUTH_PRIVATE(auth);
+
+	FREE(ad->ad_fullname, ad->ad_fullnamelen + 1);
+	FREE(ad->ad_servername, ad->ad_servernamelen + 1);
+	if (ad->ad_timehost)
+		FREE(ad->ad_timehost, strlen(ad->ad_timehost) + 1);
+	if (ad->ad_netid)
+		FREE(ad->ad_netid, strlen(ad->ad_netid) + 1);
+	if (ad->ad_uaddr)
+		FREE(ad->ad_uaddr, strlen(ad->ad_uaddr) + 1);
+	FREE(ad, sizeof (struct ad_private));
+	FREE(auth, sizeof(AUTH));
+}
+
+static struct auth_ops *
+authdes_ops(void)
+{
+	static struct auth_ops ops;
+
+	/* VARIABLES PROTECTED BY ops_lock: ops */
+ 
+	mutex_lock(&authdes_ops_lock);
+	if (ops.ah_nextverf == NULL) {
+		ops.ah_nextverf = authdes_nextverf;
+		ops.ah_marshal = authdes_marshal;
+		ops.ah_validate = authdes_validate;
+		ops.ah_refresh = authdes_refresh;
+		ops.ah_destroy = authdes_destroy;
+        }
+	mutex_unlock(&authdes_ops_lock);
+	return (&ops);
+}
diff --git a/lib/libc/rpc/authdes_prot.c b/lib/libc/rpc/authdes_prot.c
index 56b44daafe41..79a0e5baa084 100644
--- a/lib/libc/rpc/authdes_prot.c
+++ b/lib/libc/rpc/authdes_prot.c
@@ -42,16 +42,44 @@
 #include <rpc/auth_des.h>
 #include "un-namespace.h"
 
-static bool_t
-__xdr_authdes_cred(XDR *xdrs, void *cred)
+#define ATTEMPT(xdr_op) if (!(xdr_op)) return (FALSE)
+
+bool_t
+xdr_authdes_cred(XDR *xdrs, struct authdes_cred *cred)
 {
-	return (FALSE);
+	enum authdes_namekind *padc_namekind = &cred->adc_namekind;
+	/*
+	 * Unrolled xdr
+	 */
+	ATTEMPT(xdr_enum(xdrs, (enum_t *) padc_namekind));
+	switch (cred->adc_namekind) {
+	case ADN_FULLNAME:
+		ATTEMPT(xdr_string(xdrs, &cred->adc_fullname.name,
+		    MAXNETNAMELEN));
+		ATTEMPT(xdr_opaque(xdrs, (caddr_t)&cred->adc_fullname.key,
+		    sizeof(des_block)));
+		ATTEMPT(xdr_opaque(xdrs, (caddr_t)&cred->adc_fullname.window,
+		    sizeof(cred->adc_fullname.window)));
+		return (TRUE);
+	case ADN_NICKNAME:
+		ATTEMPT(xdr_opaque(xdrs, (caddr_t)&cred->adc_nickname,
+		    sizeof(cred->adc_nickname)));
+		return (TRUE);
+	default:
+		return (FALSE);
+	}
 }
-__sym_compat(xdr_authdes_cred, __xdr_authdes_cred, FBSD_1.0);
 
-static bool_t
-__xdr_authdes_verf(XDR *xdrs, void *verf)
+
+bool_t
+xdr_authdes_verf(XDR *xdrs, struct authdes_verf *verf)
 {
-	return (FALSE);
+	/*
+ 	 * Unrolled xdr
+ 	 */
+	ATTEMPT(xdr_opaque(xdrs, (caddr_t)&verf->adv_xtimestamp,
+	    sizeof(des_block)));
+	ATTEMPT(xdr_opaque(xdrs, (caddr_t)&verf->adv_int_u,
+	    sizeof(verf->adv_int_u)));
+	return (TRUE);
 }
-__sym_compat(xdr_authdes_verf, __xdr_authdes_verf, FBSD_1.0);
diff --git a/lib/libc/rpc/key_call.c b/lib/libc/rpc/key_call.c
index eb274fcfff36..5c87881c815c 100644
--- a/lib/libc/rpc/key_call.c
+++ b/lib/libc/rpc/key_call.c
@@ -32,78 +32,426 @@
  */
 
 /*
- * Secure RPC keyserver support was removed in FreeBSD 15.0.
- * These symbols are provided for backward compatibility, but provide no
- * functionality and will always return an error.
+ * key_call.c, Interface to keyserver
+ *
+ * setsecretkey(key) - set your secret key
+ * encryptsessionkey(agent, deskey) - encrypt a session key to talk to agent
+ * decryptsessionkey(agent, deskey) - decrypt ditto
+ * gendeskey(deskey) - generate a secure des key
  */
 
 #include "namespace.h"
 #include "reentrant.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
 #include <rpc/rpc.h>
-#include <rpc/key_prot.h>
 #include <rpc/auth.h>
+#include <rpc/auth_unix.h>
+#include <rpc/key_prot.h>
+#include <string.h>
+#include <netconfig.h>
+#include <sys/utsname.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/fcntl.h>
 #include "un-namespace.h"
 #include "mt_misc.h"
 
-static int
-__key_setsecret(const char *secretkey)
+
+#define	KEY_TIMEOUT	5	/* per-try timeout in seconds */
+#define	KEY_NRETRY	12	/* number of retries */
+
+#ifdef DEBUG
+#define	debug(msg)	(void) fprintf(stderr, "%s\n", msg);
+#else
+#define	debug(msg)
+#endif /* DEBUG */
+
+/*
+ * Hack to allow the keyserver to use AUTH_DES (for authenticated
+ * NIS+ calls, for example).  The only functions that get called
+ * are key_encryptsession_pk, key_decryptsession_pk, and key_gendes.
+ *
+ * The approach is to have the keyserver fill in pointers to local
+ * implementations of these functions, and to call those in key_call().
+ */
+
+cryptkeyres *(*__key_encryptsession_pk_LOCAL)(uid_t, void *arg) = 0;
+cryptkeyres *(*__key_decryptsession_pk_LOCAL)(uid_t, void *arg) = 0;
+des_block *(*__key_gendes_LOCAL)(uid_t, void *) = 0;
+
+static int key_call( u_long, xdrproc_t, void *, xdrproc_t, void *);
+
+int
+key_setsecret(const char *secretkey)
 {
-	return (-1);
+	keystatus status;
+
+	if (!key_call((u_long) KEY_SET, (xdrproc_t)xdr_keybuf,
+			(void *)secretkey,
+			(xdrproc_t)xdr_keystatus, &status)) {
+		return (-1);
+	}
+	if (status != KEY_SUCCESS) {
+		debug("set status is nonzero");
+		return (-1);
+	}
+	return (0);
 }
-__sym_compat(key_setsecret, __key_setsecret, FBSD_1.0);
 
-static int
-__key_secretkey_is_set(void)
+
+/* key_secretkey_is_set() returns 1 if the keyserver has a secret key
+ * stored for the caller's effective uid; it returns 0 otherwise
+ *
+ * N.B.:  The KEY_NET_GET key call is undocumented.  Applications shouldn't
+ * be using it, because it allows them to get the user's secret key.
+ */
+
+int
+key_secretkey_is_set(void)
 {
+	struct key_netstres 	kres;
+
+	memset((void*)&kres, 0, sizeof (kres));
+	if (key_call((u_long) KEY_NET_GET, (xdrproc_t)xdr_void, NULL,
+			(xdrproc_t)xdr_key_netstres, &kres) &&
+	    (kres.status == KEY_SUCCESS) &&
+	    (kres.key_netstres_u.knet.st_priv_key[0] != 0)) {
+		/* avoid leaving secret key in memory */
+		memset(kres.key_netstres_u.knet.st_priv_key, 0, HEXKEYBYTES);
+		return (1);
+	}
 	return (0);
 }
-__sym_compat(key_secretkey_is_set, __key_secretkey_is_set, FBSD_1.0);
 
-static int
-__key_encryptsession_pk(char *remotename, netobj *remotekey, des_block *deskey)
+int
+key_encryptsession_pk(char *remotename, netobj *remotekey, des_block *deskey)
 {
-	return (-1);
+	cryptkeyarg2 arg;
+	cryptkeyres res;
+
+	arg.remotename = remotename;
+	arg.remotekey = *remotekey;
+	arg.deskey = *deskey;
+	if (!key_call((u_long)KEY_ENCRYPT_PK, (xdrproc_t)xdr_cryptkeyarg2, &arg,
+			(xdrproc_t)xdr_cryptkeyres, &res)) {
+		return (-1);
+	}
+	if (res.status != KEY_SUCCESS) {
+		debug("encrypt status is nonzero");
+		return (-1);
+	}
+	*deskey = res.cryptkeyres_u.deskey;
+	return (0);
 }
-__sym_compat(key_encryptsession_pk, __key_encryptsession_pk, FBSD_1.0);
 
-static int
-__key_decryptsession_pk(char *remotename, netobj *remotekey, des_block *deskey)
+int
+key_decryptsession_pk(char *remotename, netobj *remotekey, des_block *deskey)
 {
-	return (-1);
+	cryptkeyarg2 arg;
+	cryptkeyres res;
+
+	arg.remotename = remotename;
+	arg.remotekey = *remotekey;
+	arg.deskey = *deskey;
+	if (!key_call((u_long)KEY_DECRYPT_PK, (xdrproc_t)xdr_cryptkeyarg2, &arg,
+			(xdrproc_t)xdr_cryptkeyres, &res)) {
+		return (-1);
+	}
+	if (res.status != KEY_SUCCESS) {
+		debug("decrypt status is nonzero");
+		return (-1);
+	}
+	*deskey = res.cryptkeyres_u.deskey;
+	return (0);
 }
-__sym_compat(key_decryptsession_pk, __key_decryptsession_pk, FBSD_1.0);
 
-static int
-__key_encryptsession(const char *remotename, des_block *deskey)
+int
+key_encryptsession(const char *remotename, des_block *deskey)
 {
-	return (-1);
+	cryptkeyarg arg;
+	cryptkeyres res;
+
+	arg.remotename = (char *) remotename;
+	arg.deskey = *deskey;
+	if (!key_call((u_long)KEY_ENCRYPT, (xdrproc_t)xdr_cryptkeyarg, &arg,
+			(xdrproc_t)xdr_cryptkeyres, &res)) {
+		return (-1);
+	}
+	if (res.status != KEY_SUCCESS) {
+		debug("encrypt status is nonzero");
+		return (-1);
+	}
+	*deskey = res.cryptkeyres_u.deskey;
+	return (0);
 }
-__sym_compat(key_encryptsession, __key_encryptsession, FBSD_1.0);
 
-static int
-__key_decryptsession(const char *remotename, des_block *deskey)
+int
+key_decryptsession(const char *remotename, des_block *deskey)
 {
*** 2748 LINES SKIPPED ***