import less(1)
[unleashed/tickless.git] / usr / src / lib / libc / port / nsl / publickey.c
blobad7f28bf0e05f2fb1da8820f31bfcab36eb9ef2e
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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.
39 * publickey.c
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
51 #include "mt.h"
52 #include "rpc_mt.h"
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <syslog.h>
57 #include <assert.h>
58 #include <sys/types.h>
59 #include <pwd.h>
60 #include "nsswitch.h"
61 #include <rpc/rpc.h>
62 #include <rpc/key_prot.h>
63 #include <rpcsvc/nis.h>
64 #include <rpcsvc/ypclnt.h>
65 #include <rpcsvc/nis_dhext.h>
66 #include <thread.h>
67 #include "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,
77 algtype_t);
79 #define WORKBUFSIZE 1024
81 extern int xdecrypt();
83 extern int __yp_match_cflookup(char *, char *, char *, int, char **,
84 int *, int *);
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};
101 #ifndef NUL
102 #define NUL '\0'
103 #endif
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);
115 * str2key
117 /* ARGSUSED */
118 static int
119 str2key(const char *instr, int lenstr,
120 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.
139 * getkey_nis()
141 * Internal implementation of getpublickey() using NIS (aka Yellow Pages,
142 * aka YP).
144 * NOTE : *** this function returns nsswitch codes and _not_ the
145 * value returned by getpublickey.
147 static int
148 getkeys_nis(int *errp, char *netname, char *pkey, char *skey, char *passwd)
150 char *domain;
151 char *keyval = NULL;
152 int keylen, err, r = 0;
153 char *p;
154 int len;
156 p = strchr(netname, '@');
157 if (!p) {
158 *errp = __NSW_UNAVAIL;
159 return (0);
162 domain = ++p;
166 * Instead of calling yp_match(), we use __yp_match_cflookup() here
167 * which has time-out control for the binding operation to nis
168 * servers.
170 err = __yp_match_cflookup(domain, (char *)PKMAP, netname,
171 strlen(netname), &keyval, &keylen, 0);
173 switch (err) {
174 case YPERR_KEY :
175 free(keyval);
176 *errp = __NSW_NOTFOUND;
177 return (0);
178 default :
179 free(keyval);
180 *errp = __NSW_UNAVAIL;
181 return (0);
182 case 0:
183 break;
186 p = strchr(keyval, ':');
187 if (p == NULL) {
188 free(keyval);
189 *errp = __NSW_NOTFOUND;
190 return (0);
192 *p = 0;
193 if (pkey) {
194 len = strlen(keyval);
195 if (len > HEXKEYBYTES) {
196 free(keyval);
197 *errp = __NSW_NOTFOUND;
198 return (0);
200 (void) strcpy(pkey, keyval);
202 r = 1;
203 p++;
204 if (skey && extract_secret(p, skey, passwd))
205 r |= 2;
206 free(keyval);
207 *errp = __NSW_SUCCESS;
208 return (r);
212 * getkey_files()
214 * The files version of getpublickey. This function attempts to
215 * get the publickey from the file PKFILE .
217 * This function defines the format of the /etc/publickey file to
218 * be :
219 * netname <whitespace> publickey:privatekey
221 * NOTE : *** this function returns nsswitch codes and _not_ the
222 * value returned by getpublickey.
225 static int
226 getkeys_files(int *errp, char *netname, char *pkey, char *skey, char *passwd)
228 char *mkey;
229 char *mval;
230 char buf[WORKBUFSIZE];
231 int r = 0;
232 char *res;
233 FILE *fd;
234 char *p;
235 char *lasts;
237 fd = fopen(PKFILE, "rF");
238 if (fd == NULL) {
239 *errp = __NSW_UNAVAIL;
240 return (0);
243 /* Search through the file linearly :-( */
244 while ((res = fgets(buf, WORKBUFSIZE, fd)) != NULL) {
246 if ((res[0] == '#') || (res[0] == '\n'))
247 continue;
248 else {
249 mkey = strtok_r(buf, "\t ", &lasts);
250 if (mkey == NULL) {
251 syslog(LOG_INFO,
252 "getpublickey: Bad record in %s for %s",
253 PKFILE, netname);
254 continue;
256 mval = strtok_r(NULL, " \t#\n", &lasts);
257 if (mval == NULL) {
258 syslog(LOG_INFO,
259 "getpublickey: Bad record in %s for %s",
260 PKFILE, netname);
261 continue;
263 /* NOTE : Case insensitive compare. */
264 if (strcasecmp(mkey, netname) == 0) {
265 p = strchr(mval, ':');
266 if (p == NULL) {
267 syslog(LOG_INFO,
268 "getpublickey: Bad record in %s for %s",
269 PKFILE, netname);
270 continue;
273 *p = 0;
274 if (pkey) {
275 int len = strlen(mval);
277 if (len > HEXKEYBYTES) {
278 syslog(LOG_INFO,
279 "getpublickey: Bad record in %s for %s",
280 PKFILE, netname);
281 continue;
283 (void) strcpy(pkey, mval);
285 r = 1;
286 p++;
287 if (skey && extract_secret(p, skey, passwd))
288 r |= 2;
289 (void) fclose(fd);
290 *errp = __NSW_SUCCESS;
291 return (r);
296 (void) fclose(fd);
297 *errp = __NSW_NOTFOUND;
298 return (0);
302 * getpublickey(netname, key)
304 * This is the actual exported interface for this function.
308 __getpublickey_cached(char *netname, char *pkey, int *from_cache)
310 return (__getpublickey_cached_g(netname, KEYSIZE, 0, pkey,
311 HEXKEYBYTES+1, from_cache));
315 getpublickey(const char *netname, char *pkey)
317 return (__getpublickey_cached((char *)netname, pkey, (int *)0));
320 void
321 __getpublickey_flush(const char *netname)
323 __getpublickey_flush_g(netname, 192, 0);
327 getsecretkey(const char *netname, char *skey, const char *passwd)
329 return (getsecretkey_g(netname, KEYSIZE, 0, skey, HEXKEYBYTES+1,
330 passwd));
334 * Routines to cache publickeys.
339 * Generic DH (any size keys) version of extract_secret.
341 static int
342 extract_secret_g(
343 char *raw, /* in */
344 char *private, /* out */
345 int prilen, /* in */
346 char *passwd, /* in */
347 char *netname, /* in */
348 keylen_t keylen, /* in */
349 algtype_t algtype) /* in */
352 return (0);
356 * extract_secret()
358 * This generic function will extract the private key
359 * from a string using the given password. Note that
360 * it uses the DES based function xdecrypt()
362 static int
363 extract_secret(char *raw, char *private, char *passwd)
365 return (extract_secret_g(raw, private, HEXKEYBYTES+1, passwd,
366 NULL, KEYSIZE, 0));
370 * getkeys_ldap_g()
372 * Fetches the key pair from LDAP. This version handles any size
373 * DH keys.
376 void
377 _nss_initf_publickey(nss_db_params_t *p)
379 p->name = NSS_DBNAM_PUBLICKEY;
380 p->default_config = NSS_DEFCONF_PUBLICKEY;
384 static int
385 getkeys_ldap_g(
386 int *err, /* in */
387 char *netname, /* in */
388 char *pkey, /* out */
389 int pkeylen, /* in */
390 char *skey, /* out */
391 int skeylen, /* in */
392 char *passwd, /* in */
393 keylen_t keylen, /* in */
394 algtype_t algtype) /* in */
396 int r = 0;
397 char *p;
398 char keytypename[NIS_MAXNAMELEN+1];
399 int len;
400 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype);
401 int rc = 0;
402 nss_XbyY_args_t arg;
403 nss_XbyY_buf_t *buf = NULL;
404 char *keyval;
406 NSS_XbyY_ALLOC(&buf, 0, NSS_BUFLEN_PUBLICKEY);
408 NSS_XbyY_INIT(&arg, buf->result, buf->buffer, buf->buflen, str2key);
409 arg.key.pkey.name = netname;
412 * LDAP stores the public and secret key info in entries using
413 * nisKeyObject objectclass. Each key is tagged with the
414 * keytype, keylength, and algorithm. The tag has the following
415 * format: {<keytype><keylength>-<algorithm>}. For example,
416 * {DH192-0}.
418 if (classic_des)
419 (void) strcpy(keytypename, "{DH192-0}");
420 else
421 (void) sprintf(keytypename, "{%s%d-%d}",
422 dh_caps_str, keylen, algtype);
423 arg.key.pkey.keytype = keytypename;
425 if (nss_search(&db_root, _nss_initf_publickey, NSS_DBOP_KEYS_BYNAME,
426 &arg) != NSS_SUCCESS) {
427 NSS_XbyY_FREE(&buf);
428 *err = __NSW_NOTFOUND;
429 return (0);
431 keyval = buf->buffer;
432 p = strchr(keyval, ':');
433 if (p == NULL) {
434 NSS_XbyY_FREE(&buf);
435 *err = __NSW_NOTFOUND;
436 return (0);
438 *p = 0;
439 if (pkey) {
440 len = strlen(keyval);
441 if (len > HEXKEYBYTES) {
442 NSS_XbyY_FREE(&buf);
443 *err = __NSW_NOTFOUND;
444 return (0);
446 (void) strcpy(pkey, keyval);
448 r = 1;
449 p++;
450 if (skey && extract_secret(p, skey, passwd))
451 r |= 2;
452 NSS_XbyY_FREE(&buf);
453 *err = __NSW_SUCCESS;
454 return (r);
459 * Convert a netname to a name we will hash on. For classic_des,
460 * just copy netname as is. But for new and improved ("now in
461 * new longer sizes!") DHEXT, add a ":keylen-algtype" suffix to hash on.
463 * Returns the hashname string on success or NULL on failure.
465 static char *
466 netname2hashname(
467 const char *netname,
468 char *hashname,
469 int bufsiz,
470 keylen_t keylen,
471 algtype_t algtype)
473 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype);
475 if (!netname || !hashname || !bufsiz)
476 return (NULL);
478 if (classic_des) {
479 if (bufsiz > strlen(netname))
480 (void) strcpy(hashname, netname);
481 else
482 return (NULL);
483 } else {
484 char tmp[128];
485 (void) sprintf(tmp, ":%d-%d", keylen, algtype);
486 if (bufsiz > (strlen(netname) + strlen(tmp)))
487 (void) sprintf(hashname, "%s%s", netname, tmp);
488 else
489 return (NULL);
492 return (hashname);
496 * Flush netname's publickey of the given key length and algorithm type.
498 void
499 __getpublickey_flush_g(const char *netname, keylen_t keylen, algtype_t algtype)
501 char *p, hashname[MAXNETNAMELEN+1];
502 p = netname2hashname(netname, hashname, MAXNETNAMELEN, keylen, algtype);
506 * Generic DH (any size keys) version of __getpublickey_cached.
509 __getpublickey_cached_g(const char netname[], /* in */
510 keylen_t keylen, /* in */
511 algtype_t algtype, /* in */
512 char *pkey, /* out */
513 size_t pkeylen, /* in */
514 int *from_cache) /* in/out */
516 int needfree = 1, res, err;
517 struct __nsw_switchconfig *conf;
518 struct __nsw_lookup *look;
519 enum __nsw_parse_err perr;
520 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype);
521 int retry_cache = 0;
523 if (!netname || !pkey)
524 return (0);
526 conf = __nsw_getconfig("publickey", &perr);
527 if (!conf) {
528 conf = &publickey_default;
529 needfree = 0;
531 for (look = conf->lookups; look; look = look->next) {
532 if (strcmp(look->service_name, "ldap") == 0) {
533 res = getkeys_ldap_g(&err, (char *)netname,
534 pkey, pkeylen, NULL, 0, NULL,
535 keylen, algtype);
536 /* long DH keys will not be in nis or files */
537 } else if (classic_des &&
538 strcmp(look->service_name, "nis") == 0)
539 res = getkeys_nis(&err, (char *)netname, pkey,
540 NULL, NULL);
541 else if (classic_des &&
542 strcmp(look->service_name, "files") == 0)
543 res = getkeys_files(&err, (char *)netname, pkey,
544 NULL, NULL);
545 else {
546 syslog(LOG_INFO, "Unknown publickey nameservice '%s'",
547 look->service_name);
548 err = __NSW_UNAVAIL;
549 res = 0;
552 switch (look->actions[err]) {
553 case __NSW_CONTINUE :
554 continue;
555 case __NSW_RETURN :
556 if (needfree)
557 __nsw_freeconfig(conf);
558 return ((res & 1) != 0);
559 default :
560 syslog(LOG_INFO, "Unknown action for nameservice %s",
561 look->service_name);
565 if (needfree)
566 __nsw_freeconfig(conf);
567 return (0);
573 * Generic (all sizes) DH version of getpublickey.
576 getpublickey_g(
577 const char *netname, /* in */
578 int keylen, /* in */
579 int algtype, /* in */
580 char *pkey, /* out */
581 size_t pkeylen) /* in */
583 return (__getpublickey_cached_g(netname, keylen, algtype, pkey,
584 pkeylen, (int *)0));
588 * Generic (all sizes) DH version of getsecretkey_g.
591 getsecretkey_g(
592 const char *netname, /* in */
593 keylen_t keylen, /* in */
594 algtype_t algtype, /* in */
595 char *skey, /* out */
596 size_t skeylen, /* in */
597 const char *passwd) /* in */
599 int needfree = 1, res, err;
600 struct __nsw_switchconfig *conf;
601 struct __nsw_lookup *look;
602 enum __nsw_parse_err perr;
603 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype);
605 if (!netname || !skey || !skeylen)
606 return (0);
608 conf = __nsw_getconfig("publickey", &perr);
610 if (!conf) {
611 conf = &publickey_default;
612 needfree = 0;
615 for (look = conf->lookups; look; look = look->next) {
616 if (strcmp(look->service_name, "ldap") == 0)
617 res = getkeys_ldap_g(&err, (char *)netname,
618 NULL, 0, skey, skeylen,
619 (char *)passwd, keylen, algtype);
620 /* long DH keys will not be in nis or files */
621 else if (classic_des && strcmp(look->service_name, "nis") == 0)
622 res = getkeys_nis(&err, (char *)netname,
623 NULL, skey, (char *)passwd);
624 else if (classic_des &&
625 strcmp(look->service_name, "files") == 0)
626 res = getkeys_files(&err, (char *)netname,
627 NULL, skey, (char *)passwd);
628 else {
629 syslog(LOG_INFO, "Unknown publickey nameservice '%s'",
630 look->service_name);
631 err = __NSW_UNAVAIL;
632 res = 0;
634 switch (look->actions[err]) {
635 case __NSW_CONTINUE :
636 continue;
637 case __NSW_RETURN :
638 if (needfree)
639 __nsw_freeconfig(conf);
640 return ((res & 2) != 0);
641 default :
642 syslog(LOG_INFO, "Unknown action for nameservice %s",
643 look->service_name);
646 if (needfree)
647 __nsw_freeconfig(conf);
648 return (0);