4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
30 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
31 /* All Rights Reserved */
34 * Portions of this source code were derived from Berkeley 4.3 BSD
35 * under license from the Regents of the University of California.
42 * Public and Private (secret) key lookup routines. These functions
43 * are used by the secure RPC auth_des flavor to get the public and
44 * private keys for secure RPC principals. Originally designed to
45 * talk only to YP, AT&T modified them to talk to files, and now
46 * they can also talk to NIS+. The policy for these lookups is now
47 * defined in terms of the nameservice switch as follows :
48 * publickey: nis files
52 #include "../rpc/rpc_mt.h"
58 #include <sys/types.h>
62 #include <rpc/key_prot.h>
63 #include <rpcsvc/nis.h>
64 #include <rpcsvc/ypclnt.h>
65 #include <rpcsvc/nis_dhext.h>
67 #include "../nis/gen/nis_clnt.h"
68 #include <nss_dbdefs.h>
71 static const char *PKMAP
= "publickey.byname";
72 static const char *PKFILE
= "/etc/publickey";
73 static const char dh_caps_str
[] = "DH";
74 static const char des_caps_str
[] = AUTH_DES_AUTH_TYPE
;
76 static char *netname2hashname(const char *, char *, int, keylen_t
,
79 #define WORKBUFSIZE 1024
81 extern int xdecrypt();
83 extern int __yp_match_cflookup(char *, char *, char *, int, char **,
88 * default publickey policy:
89 * publickey: nis [NOTFOUND = return] files
93 /* NSW_NOTSUCCESS NSW_NOTFOUND NSW_UNAVAIL NSW_TRYAGAIN */
94 #define DEF_ACTION {__NSW_RETURN, __NSW_RETURN, __NSW_CONTINUE, __NSW_CONTINUE}
96 static struct __nsw_lookup lookup_files
= {"files", DEF_ACTION
, NULL
, NULL
},
97 lookup_nis
= {"nis", DEF_ACTION
, NULL
, &lookup_files
};
98 static struct __nsw_switchconfig publickey_default
=
99 {0, "publickey", 2, &lookup_nis
};
105 extern mutex_t serialize_pkey
;
107 static int extract_secret();
110 * db_root is used for switch backends.
112 static DEFINE_NSS_DB_ROOT(db_root
);
119 str2key(const char *instr
, int lenstr
, void *ent
, char *buffer
, int buflen
)
121 if (lenstr
+ 1 > buflen
)
122 return (NSS_STR_PARSE_ERANGE
);
124 * We copy the input string into the output buffer
126 (void) memcpy(buffer
, instr
, lenstr
);
127 buffer
[lenstr
] = '\0';
129 return (NSS_STR_PARSE_SUCCESS
);
132 * These functions are the "backends" for the switch for public keys. They
133 * get both the public and private keys from each of the supported name
134 * services (nis, files). They are passed the appropriate parameters
135 * and return 0 if unsuccessful with *errp set, or 1 when they got just the
136 * public key and 3 when they got both the public and private keys.
141 * Internal implementation of getpublickey() using NIS (aka Yellow Pages,
144 * NOTE : *** this function returns nsswitch codes and _not_ the
145 * value returned by getpublickey.
148 getkeys_nis(int *errp
, char *netname
, char *pkey
, char *skey
, char *passwd
)
152 int keylen
, err
, r
= 0;
156 p
= strchr(netname
, '@');
158 *errp
= __NSW_UNAVAIL
;
166 * Instead of calling yp_match(), we use __yp_match_cflookup() here
167 * which has time-out control for the binding operation to nis
170 err
= __yp_match_cflookup(domain
, (char *)PKMAP
, netname
,
171 strlen(netname
), &keyval
, &keylen
, 0);
177 *errp
= __NSW_NOTFOUND
;
182 *errp
= __NSW_UNAVAIL
;
188 p
= strchr(keyval
, ':');
191 *errp
= __NSW_NOTFOUND
;
196 len
= strlen(keyval
);
197 if (len
> HEXKEYBYTES
) {
199 *errp
= __NSW_NOTFOUND
;
202 (void) strcpy(pkey
, keyval
);
206 if (skey
&& extract_secret(p
, skey
, passwd
))
209 *errp
= __NSW_SUCCESS
;
216 * The files version of getpublickey. This function attempts to
217 * get the publickey from the file PKFILE .
219 * This function defines the format of the /etc/publickey file to
221 * netname <whitespace> publickey:privatekey
223 * NOTE : *** this function returns nsswitch codes and _not_ the
224 * value returned by getpublickey.
228 getkeys_files(int *errp
, char *netname
, char *pkey
, char *skey
, char *passwd
)
232 char buf
[WORKBUFSIZE
];
239 fd
= fopen(PKFILE
, "rF");
241 *errp
= __NSW_UNAVAIL
;
245 /* Search through the file linearly :-( */
246 while ((res
= fgets(buf
, WORKBUFSIZE
, fd
)) != NULL
) {
248 if ((res
[0] == '#') || (res
[0] == '\n'))
251 mkey
= strtok_r(buf
, "\t ", &lasts
);
254 "getpublickey: Bad record in %s for %s",
258 mval
= strtok_r(NULL
, " \t#\n", &lasts
);
261 "getpublickey: Bad record in %s for %s",
265 /* NOTE : Case insensitive compare. */
266 if (strcasecmp(mkey
, netname
) == 0) {
267 p
= strchr(mval
, ':');
270 "getpublickey: Bad record in %s for %s",
277 int len
= strlen(mval
);
279 if (len
> HEXKEYBYTES
) {
281 "getpublickey: Bad record in %s for %s",
285 (void) strcpy(pkey
, mval
);
289 if (skey
&& extract_secret(p
, skey
, passwd
))
292 *errp
= __NSW_SUCCESS
;
299 *errp
= __NSW_NOTFOUND
;
304 * getpublickey(netname, key)
306 * This is the actual exported interface for this function.
310 __getpublickey_cached(char *netname
, char *pkey
, int *from_cache
)
312 return (__getpublickey_cached_g(netname
, KEYSIZE
, 0, pkey
,
313 HEXKEYBYTES
+1, from_cache
));
317 getpublickey(const char *netname
, char *pkey
)
319 return (__getpublickey_cached((char *)netname
, pkey
, (int *)0));
323 __getpublickey_flush(const char *netname
)
325 __getpublickey_flush_g(netname
, 192, 0);
329 getsecretkey(const char *netname
, char *skey
, const char *passwd
)
331 return (getsecretkey_g(netname
, KEYSIZE
, 0, skey
, HEXKEYBYTES
+1,
336 * Routines to cache publickeys.
341 * Generic DH (any size keys) version of extract_secret.
346 char *private, /* out */
348 char *passwd
, /* in */
349 char *netname
, /* in */
350 keylen_t keylen
, /* in */
351 algtype_t algtype
) /* in */
353 char *buf
= malloc(strlen(raw
) + 1); /* private tmp buf */
356 if (!buf
|| !passwd
|| !raw
|| !private || !prilen
||
357 !VALID_KEYALG(keylen
, algtype
)) {
365 (void) strcpy(buf
, raw
);
367 /* strip off pesky colon if it exists */
368 p
= strchr(buf
, ':');
373 /* raw buf has chksum appended, so let's verify it too */
374 if (!xdecrypt_g(buf
, keylen
, algtype
, passwd
, netname
, TRUE
)) {
377 return (1); /* yes, return 1 even if xdecrypt fails */
380 if (strlen(buf
) >= prilen
) {
386 (void) strcpy(private, buf
);
394 * This generic function will extract the private key
395 * from a string using the given password. Note that
396 * it uses the DES based function xdecrypt()
399 extract_secret(char *raw
, char *private, char *passwd
)
401 return (extract_secret_g(raw
, private, HEXKEYBYTES
+1, passwd
,
408 * Fetches the key pair from LDAP. This version handles any size
413 _nss_initf_publickey(nss_db_params_t
*p
)
415 p
->name
= NSS_DBNAM_PUBLICKEY
;
416 p
->default_config
= NSS_DEFCONF_PUBLICKEY
;
423 char *netname
, /* in */
424 char *pkey
, /* out */
425 int pkeylen
, /* in */
426 char *skey
, /* out */
427 int skeylen
, /* in */
428 char *passwd
, /* in */
429 keylen_t keylen
, /* in */
430 algtype_t algtype
) /* in */
434 char keytypename
[NIS_MAXNAMELEN
+1];
436 const bool_t classic_des
= AUTH_DES_KEY(keylen
, algtype
);
439 nss_XbyY_buf_t
*buf
= NULL
;
442 NSS_XbyY_ALLOC(&buf
, 0, NSS_BUFLEN_PUBLICKEY
);
444 NSS_XbyY_INIT(&arg
, buf
->result
, buf
->buffer
, buf
->buflen
, str2key
);
445 arg
.key
.pkey
.name
= netname
;
448 * LDAP stores the public and secret key info in entries using
449 * nisKeyObject objectclass. Each key is tagged with the
450 * keytype, keylength, and algorithm. The tag has the following
451 * format: {<keytype><keylength>-<algorithm>}. For example,
455 (void) strcpy(keytypename
, "{DH192-0}");
457 (void) sprintf(keytypename
, "{%s%d-%d}",
458 dh_caps_str
, keylen
, algtype
);
459 arg
.key
.pkey
.keytype
= keytypename
;
461 if (nss_search(&db_root
, _nss_initf_publickey
, NSS_DBOP_KEYS_BYNAME
,
462 &arg
) != NSS_SUCCESS
) {
464 *err
= __NSW_NOTFOUND
;
467 keyval
= buf
->buffer
;
468 p
= strchr(keyval
, ':');
471 *err
= __NSW_NOTFOUND
;
476 len
= strlen(keyval
);
477 if (len
> HEXKEYBYTES
) {
479 *err
= __NSW_NOTFOUND
;
482 (void) strcpy(pkey
, keyval
);
486 if (skey
&& extract_secret(p
, skey
, passwd
))
489 *err
= __NSW_SUCCESS
;
495 * Convert a netname to a name we will hash on. For classic_des,
496 * just copy netname as is. But for new and improved ("now in
497 * new longer sizes!") DHEXT, add a ":keylen-algtype" suffix to hash on.
499 * Returns the hashname string on success or NULL on failure.
509 const bool_t classic_des
= AUTH_DES_KEY(keylen
, algtype
);
511 if (!netname
|| !hashname
|| !bufsiz
)
515 if (bufsiz
> strlen(netname
))
516 (void) strcpy(hashname
, netname
);
521 (void) sprintf(tmp
, ":%d-%d", keylen
, algtype
);
522 if (bufsiz
> (strlen(netname
) + strlen(tmp
)))
523 (void) sprintf(hashname
, "%s%s", netname
, tmp
);
532 * Flush netname's publickey of the given key length and algorithm type.
535 __getpublickey_flush_g(const char *netname
, keylen_t keylen
, algtype_t algtype
)
537 char hashname
[MAXNETNAMELEN
+1];
539 (void) netname2hashname(netname
, hashname
, MAXNETNAMELEN
, keylen
,
544 * Generic DH (any size keys) version of __getpublickey_cached.
547 __getpublickey_cached_g(const char netname
[], /* in */
548 keylen_t keylen
, /* in */
549 algtype_t algtype
, /* in */
550 char *pkey
, /* out */
551 size_t pkeylen
, /* in */
552 int *from_cache
) /* in/out */
554 int needfree
= 1, res
, err
;
555 struct __nsw_switchconfig
*conf
;
556 struct __nsw_lookup
*look
;
557 enum __nsw_parse_err perr
;
558 const bool_t classic_des
= AUTH_DES_KEY(keylen
, algtype
);
561 if (!netname
|| !pkey
)
564 conf
= __nsw_getconfig("publickey", &perr
);
566 conf
= &publickey_default
;
569 for (look
= conf
->lookups
; look
; look
= look
->next
) {
570 if (strcmp(look
->service_name
, "ldap") == 0) {
571 res
= getkeys_ldap_g(&err
, (char *)netname
,
572 pkey
, pkeylen
, NULL
, 0, NULL
,
574 /* long DH keys will not be in nis or files */
575 } else if (classic_des
&&
576 strcmp(look
->service_name
, "nis") == 0)
577 res
= getkeys_nis(&err
, (char *)netname
, pkey
,
579 else if (classic_des
&&
580 strcmp(look
->service_name
, "files") == 0)
581 res
= getkeys_files(&err
, (char *)netname
, pkey
,
584 syslog(LOG_INFO
, "Unknown publickey nameservice '%s'",
590 switch (look
->actions
[err
]) {
591 case __NSW_CONTINUE
:
595 __nsw_freeconfig(conf
);
596 return ((res
& 1) != 0);
598 syslog(LOG_INFO
, "Unknown action for nameservice %s",
604 __nsw_freeconfig(conf
);
611 * Generic (all sizes) DH version of getpublickey.
615 const char *netname
, /* in */
617 int algtype
, /* in */
618 char *pkey
, /* out */
619 size_t pkeylen
) /* in */
621 return (__getpublickey_cached_g(netname
, keylen
, algtype
, pkey
,
626 * Generic (all sizes) DH version of getsecretkey_g.
630 const char *netname
, /* in */
631 keylen_t keylen
, /* in */
632 algtype_t algtype
, /* in */
633 char *skey
, /* out */
634 size_t skeylen
, /* in */
635 const char *passwd
) /* in */
637 int needfree
= 1, res
, err
;
638 struct __nsw_switchconfig
*conf
;
639 struct __nsw_lookup
*look
;
640 enum __nsw_parse_err perr
;
641 const bool_t classic_des
= AUTH_DES_KEY(keylen
, algtype
);
643 if (!netname
|| !skey
|| !skeylen
)
646 conf
= __nsw_getconfig("publickey", &perr
);
649 conf
= &publickey_default
;
653 for (look
= conf
->lookups
; look
; look
= look
->next
) {
654 if (strcmp(look
->service_name
, "ldap") == 0)
655 res
= getkeys_ldap_g(&err
, (char *)netname
,
656 NULL
, 0, skey
, skeylen
,
657 (char *)passwd
, keylen
, algtype
);
658 /* long DH keys will not be in nis or files */
659 else if (classic_des
&& strcmp(look
->service_name
, "nis") == 0)
660 res
= getkeys_nis(&err
, (char *)netname
,
661 NULL
, skey
, (char *)passwd
);
662 else if (classic_des
&&
663 strcmp(look
->service_name
, "files") == 0)
664 res
= getkeys_files(&err
, (char *)netname
,
665 NULL
, skey
, (char *)passwd
);
667 syslog(LOG_INFO
, "Unknown publickey nameservice '%s'",
672 switch (look
->actions
[err
]) {
673 case __NSW_CONTINUE
:
677 __nsw_freeconfig(conf
);
678 return ((res
& 2) != 0);
680 syslog(LOG_INFO
, "Unknown action for nameservice %s",
685 __nsw_freeconfig(conf
);