1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29
30 /*
31 * Portions of this source code were derived from Berkeley 4.3 BSD
32 * under license from the Regents of the University of California.
33 */
34
35 /*
36 * publickey.c
37 *
38 *
39 * Public and Private (secret) key lookup routines. These functions
40 * are used by the secure RPC auth_des flavor to get the public and
41 * private keys for secure RPC principals. Originally designed to
42 * talk only to YP, AT&T modified them to talk to files, and now
43 * they can also talk to NIS+. The policy for these lookups is now
44 * defined in terms of the nameservice switch as follows :
45 * publickey: nis files
46 *
47 */
48 #include "mt.h"
49 #include "../rpc/rpc_mt.h"
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <syslog.h>
54 #include <assert.h>
55 #include <sys/types.h>
56 #include <pwd.h>
57 #include "nsswitch.h"
58 #include <rpc/rpc.h>
59 #include <rpc/key_prot.h>
60 #include <rpcsvc/nis.h>
61 #include <rpcsvc/ypclnt.h>
62 #include <rpcsvc/nis_dhext.h>
63 #include <thread.h>
64 #include "../nis/gen/nis_clnt.h"
65 #include <nss_dbdefs.h>
66
67
68 static const char *PKMAP = "publickey.byname";
69 static const char *PKFILE = "/etc/publickey";
70 static const char dh_caps_str[] = "DH";
71 static const char des_caps_str[] = AUTH_DES_AUTH_TYPE;
72
73 static char *netname2hashname(const char *, char *, int, keylen_t,
74 algtype_t);
75
76 #define WORKBUFSIZE 1024
77
78 extern int xdecrypt();
79
80 extern int __yp_match_cflookup(char *, char *, char *, int, char **,
81 int *, int *);
82
83
84 /*
85 * default publickey policy:
86 * publickey: nis [NOTFOUND = return] files
87 */
88
89
90 /* NSW_NOTSUCCESS NSW_NOTFOUND NSW_UNAVAIL NSW_TRYAGAIN */
91 #define DEF_ACTION {__NSW_RETURN, __NSW_RETURN, __NSW_CONTINUE, __NSW_CONTINUE}
92
93 static struct __nsw_lookup lookup_files = {"files", DEF_ACTION, NULL, NULL},
94 lookup_nis = {"nis", DEF_ACTION, NULL, &lookup_files};
95 static struct __nsw_switchconfig publickey_default =
96 {0, "publickey", 2, &lookup_nis};
97
98 #ifndef NUL
99 #define NUL '\0'
100 #endif
101
102 extern mutex_t serialize_pkey;
103
104 static int extract_secret();
105
106 /*
107 * db_root is used for switch backends.
108 */
109 static DEFINE_NSS_DB_ROOT(db_root);
110
111 /*
112 * str2key
113 */
114 /* ARGSUSED */
115 static int
str2key(const char * instr,int lenstr,void * ent,char * buffer,int buflen)116 str2key(const char *instr, int lenstr,
117 void *ent, char *buffer, int buflen) {
118 if (lenstr + 1 > buflen)
119 return (NSS_STR_PARSE_ERANGE);
120 /*
121 * We copy the input string into the output buffer
122 */
123 (void) memcpy(buffer, instr, lenstr);
124 buffer[lenstr] = '\0';
125
126 return (NSS_STR_PARSE_SUCCESS);
127 }
128 /*
129 * These functions are the "backends" for the switch for public keys. They
130 * get both the public and private keys from each of the supported name
131 * services (nis, files). They are passed the appropriate parameters
132 * and return 0 if unsuccessful with *errp set, or 1 when they got just the
133 * public key and 3 when they got both the public and private keys.
134 *
135 *
136 * getkey_nis()
137 *
138 * Internal implementation of getpublickey() using NIS (aka Yellow Pages,
139 * aka YP).
140 *
141 * NOTE : *** this function returns nsswitch codes and _not_ the
142 * value returned by getpublickey.
143 */
144 static int
getkeys_nis(int * errp,char * netname,char * pkey,char * skey,char * passwd)145 getkeys_nis(int *errp, char *netname, char *pkey, char *skey, char *passwd)
146 {
147 char *domain;
148 char *keyval = NULL;
149 int keylen, err, r = 0;
150 char *p;
151 int len;
152
153 p = strchr(netname, '@');
154 if (!p) {
155 *errp = __NSW_UNAVAIL;
156 return (0);
157 }
158
159 domain = ++p;
160
161
162 /*
163 * Instead of calling yp_match(), we use __yp_match_cflookup() here
164 * which has time-out control for the binding operation to nis
165 * servers.
166 */
167 err = __yp_match_cflookup(domain, (char *)PKMAP, netname,
168 strlen(netname), &keyval, &keylen, 0);
169
170 switch (err) {
171 case YPERR_KEY :
172 if (keyval)
173 free(keyval);
174 *errp = __NSW_NOTFOUND;
175 return (0);
176 default :
177 if (keyval)
178 free(keyval);
179 *errp = __NSW_UNAVAIL;
180 return (0);
181 case 0:
182 break;
183 }
184
185 p = strchr(keyval, ':');
186 if (p == NULL) {
187 free(keyval);
188 *errp = __NSW_NOTFOUND;
189 return (0);
190 }
191 *p = 0;
192 if (pkey) {
193 len = strlen(keyval);
194 if (len > HEXKEYBYTES) {
195 free(keyval);
196 *errp = __NSW_NOTFOUND;
197 return (0);
198 }
199 (void) strcpy(pkey, keyval);
200 }
201 r = 1;
202 p++;
203 if (skey && extract_secret(p, skey, passwd))
204 r |= 2;
205 free(keyval);
206 *errp = __NSW_SUCCESS;
207 return (r);
208 }
209
210 /*
211 * getkey_files()
212 *
213 * The files version of getpublickey. This function attempts to
214 * get the publickey from the file PKFILE .
215 *
216 * This function defines the format of the /etc/publickey file to
217 * be :
218 * netname <whitespace> publickey:privatekey
219 *
220 * NOTE : *** this function returns nsswitch codes and _not_ the
221 * value returned by getpublickey.
222 */
223
224 static int
getkeys_files(int * errp,char * netname,char * pkey,char * skey,char * passwd)225 getkeys_files(int *errp, char *netname, char *pkey, char *skey, char *passwd)
226 {
227 char *mkey;
228 char *mval;
229 char buf[WORKBUFSIZE];
230 int r = 0;
231 char *res;
232 FILE *fd;
233 char *p;
234 char *lasts;
235
236 fd = fopen(PKFILE, "rF");
237 if (fd == NULL) {
238 *errp = __NSW_UNAVAIL;
239 return (0);
240 }
241
242 /* Search through the file linearly :-( */
243 while ((res = fgets(buf, WORKBUFSIZE, fd)) != NULL) {
244
245 if ((res[0] == '#') || (res[0] == '\n'))
246 continue;
247 else {
248 mkey = strtok_r(buf, "\t ", &lasts);
249 if (mkey == NULL) {
250 syslog(LOG_INFO,
251 "getpublickey: Bad record in %s for %s",
252 PKFILE, netname);
253 continue;
254 }
255 mval = strtok_r(NULL, " \t#\n", &lasts);
256 if (mval == NULL) {
257 syslog(LOG_INFO,
258 "getpublickey: Bad record in %s for %s",
259 PKFILE, netname);
260 continue;
261 }
262 /* NOTE : Case insensitive compare. */
263 if (strcasecmp(mkey, netname) == 0) {
264 p = strchr(mval, ':');
265 if (p == NULL) {
266 syslog(LOG_INFO,
267 "getpublickey: Bad record in %s for %s",
268 PKFILE, netname);
269 continue;
270 }
271
272 *p = 0;
273 if (pkey) {
274 int len = strlen(mval);
275
276 if (len > HEXKEYBYTES) {
277 syslog(LOG_INFO,
278 "getpublickey: Bad record in %s for %s",
279 PKFILE, netname);
280 continue;
281 }
282 (void) strcpy(pkey, mval);
283 }
284 r = 1;
285 p++;
286 if (skey && extract_secret(p, skey, passwd))
287 r |= 2;
288 (void) fclose(fd);
289 *errp = __NSW_SUCCESS;
290 return (r);
291 }
292 }
293 }
294
295 (void) fclose(fd);
296 *errp = __NSW_NOTFOUND;
297 return (0);
298 }
299
300 /*
301 * getpublickey(netname, key)
302 *
303 * This is the actual exported interface for this function.
304 */
305
306 int
__getpublickey_cached(char * netname,char * pkey,int * from_cache)307 __getpublickey_cached(char *netname, char *pkey, int *from_cache)
308 {
309 return (__getpublickey_cached_g(netname, KEYSIZE, 0, pkey,
310 HEXKEYBYTES+1, from_cache));
311 }
312
313 int
getpublickey(const char * netname,char * pkey)314 getpublickey(const char *netname, char *pkey)
315 {
316 return (__getpublickey_cached((char *)netname, pkey, (int *)0));
317 }
318
319 void
__getpublickey_flush(const char * netname)320 __getpublickey_flush(const char *netname)
321 {
322 __getpublickey_flush_g(netname, 192, 0);
323 }
324
325 int
getsecretkey(const char * netname,char * skey,const char * passwd)326 getsecretkey(const char *netname, char *skey, const char *passwd)
327 {
328 return (getsecretkey_g(netname, KEYSIZE, 0, skey, HEXKEYBYTES+1,
329 passwd));
330 }
331
332 /*
333 * Routines to cache publickeys.
334 */
335
336
337 /*
338 * Generic DH (any size keys) version of extract_secret.
339 */
340 static int
extract_secret_g(char * raw,char * private,int prilen,char * passwd,char * netname,keylen_t keylen,algtype_t algtype)341 extract_secret_g(
342 char *raw, /* in */
343 char *private, /* out */
344 int prilen, /* in */
345 char *passwd, /* in */
346 char *netname, /* in */
347 keylen_t keylen, /* in */
348 algtype_t algtype) /* in */
349
350 {
351 char *buf = malloc(strlen(raw) + 1); /* private tmp buf */
352 char *p;
353
354 if (!buf || !passwd || !raw || !private || !prilen ||
355 !VALID_KEYALG(keylen, algtype)) {
356 if (private)
357 *private = NUL;
358 if (buf)
359 free(buf);
360 return (0);
361 }
362
363 (void) strcpy(buf, raw);
364
365 /* strip off pesky colon if it exists */
366 p = strchr(buf, ':');
367 if (p) {
368 *p = 0;
369 }
370
371 /* raw buf has chksum appended, so let's verify it too */
372 if (!xdecrypt_g(buf, keylen, algtype, passwd, netname, TRUE)) {
373 private[0] = 0;
374 free(buf);
375 return (1); /* yes, return 1 even if xdecrypt fails */
376 }
377
378 if (strlen(buf) >= prilen) {
379 private[0] = 0;
380 free(buf);
381 return (0);
382 }
383
384 (void) strcpy(private, buf);
385 free(buf);
386 return (1);
387 }
388
389 /*
390 * extract_secret()
391 *
392 * This generic function will extract the private key
393 * from a string using the given password. Note that
394 * it uses the DES based function xdecrypt()
395 */
396 static int
extract_secret(char * raw,char * private,char * passwd)397 extract_secret(char *raw, char *private, char *passwd)
398 {
399 return (extract_secret_g(raw, private, HEXKEYBYTES+1, passwd,
400 NULL, KEYSIZE, 0));
401 }
402
403 /*
404 * getkeys_ldap_g()
405 *
406 * Fetches the key pair from LDAP. This version handles any size
407 * DH keys.
408 */
409
410 void
_nss_initf_publickey(nss_db_params_t * p)411 _nss_initf_publickey(nss_db_params_t *p)
412 {
413 p->name = NSS_DBNAM_PUBLICKEY;
414 p->default_config = NSS_DEFCONF_PUBLICKEY;
415 }
416
417
418 static int
getkeys_ldap_g(int * err,char * netname,char * pkey,int pkeylen,char * skey,int skeylen,char * passwd,keylen_t keylen,algtype_t algtype)419 getkeys_ldap_g(
420 int *err, /* in */
421 char *netname, /* in */
422 char *pkey, /* out */
423 int pkeylen, /* in */
424 char *skey, /* out */
425 int skeylen, /* in */
426 char *passwd, /* in */
427 keylen_t keylen, /* in */
428 algtype_t algtype) /* in */
429 {
430 int r = 0;
431 char *p;
432 char keytypename[NIS_MAXNAMELEN+1];
433 int len;
434 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype);
435 int rc = 0;
436 nss_XbyY_args_t arg;
437 nss_XbyY_buf_t *buf = NULL;
438 char *keyval;
439
440 NSS_XbyY_ALLOC(&buf, 0, NSS_BUFLEN_PUBLICKEY);
441
442 NSS_XbyY_INIT(&arg, buf->result, buf->buffer, buf->buflen, str2key);
443 arg.key.pkey.name = netname;
444
445 /*
446 * LDAP stores the public and secret key info in entries using
447 * nisKeyObject objectclass. Each key is tagged with the
448 * keytype, keylength, and algorithm. The tag has the following
449 * format: {<keytype><keylength>-<algorithm>}. For example,
450 * {DH192-0}.
451 */
452 if (classic_des)
453 (void) strcpy(keytypename, "{DH192-0}");
454 else
455 (void) sprintf(keytypename, "{%s%d-%d}",
456 dh_caps_str, keylen, algtype);
457 arg.key.pkey.keytype = keytypename;
458
459 if (nss_search(&db_root, _nss_initf_publickey, NSS_DBOP_KEYS_BYNAME,
460 &arg) != NSS_SUCCESS) {
461 NSS_XbyY_FREE(&buf);
462 *err = __NSW_NOTFOUND;
463 return (0);
464 }
465 keyval = buf->buffer;
466 p = strchr(keyval, ':');
467 if (p == NULL) {
468 NSS_XbyY_FREE(&buf);
469 *err = __NSW_NOTFOUND;
470 return (0);
471 }
472 *p = 0;
473 if (pkey) {
474 len = strlen(keyval);
475 if (len > HEXKEYBYTES) {
476 NSS_XbyY_FREE(&buf);
477 *err = __NSW_NOTFOUND;
478 return (0);
479 }
480 (void) strcpy(pkey, keyval);
481 }
482 r = 1;
483 p++;
484 if (skey && extract_secret(p, skey, passwd))
485 r |= 2;
486 NSS_XbyY_FREE(&buf);
487 *err = __NSW_SUCCESS;
488 return (r);
489 }
490
491
492 /*
493 * Convert a netname to a name we will hash on. For classic_des,
494 * just copy netname as is. But for new and improved ("now in
495 * new longer sizes!") DHEXT, add a ":keylen-algtype" suffix to hash on.
496 *
497 * Returns the hashname string on success or NULL on failure.
498 */
499 static char *
netname2hashname(const char * netname,char * hashname,int bufsiz,keylen_t keylen,algtype_t algtype)500 netname2hashname(
501 const char *netname,
502 char *hashname,
503 int bufsiz,
504 keylen_t keylen,
505 algtype_t algtype)
506 {
507 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype);
508
509 if (!netname || !hashname || !bufsiz)
510 return (NULL);
511
512 if (classic_des) {
513 if (bufsiz > strlen(netname))
514 (void) strcpy(hashname, netname);
515 else
516 return (NULL);
517 } else {
518 char tmp[128];
519 (void) sprintf(tmp, ":%d-%d", keylen, algtype);
520 if (bufsiz > (strlen(netname) + strlen(tmp)))
521 (void) sprintf(hashname, "%s%s", netname, tmp);
522 else
523 return (NULL);
524 }
525
526 return (hashname);
527 }
528
529 /*
530 * Flush netname's publickey of the given key length and algorithm type.
531 */
532 void
__getpublickey_flush_g(const char * netname,keylen_t keylen,algtype_t algtype)533 __getpublickey_flush_g(const char *netname, keylen_t keylen, algtype_t algtype)
534 {
535 char *p, hashname[MAXNETNAMELEN+1];
536 p = netname2hashname(netname, hashname, MAXNETNAMELEN, keylen, algtype);
537 }
538
539 /*
540 * Generic DH (any size keys) version of __getpublickey_cached.
541 */
542 int
__getpublickey_cached_g(const char netname[],keylen_t keylen,algtype_t algtype,char * pkey,size_t pkeylen,int * from_cache)543 __getpublickey_cached_g(const char netname[], /* in */
544 keylen_t keylen, /* in */
545 algtype_t algtype, /* in */
546 char *pkey, /* out */
547 size_t pkeylen, /* in */
548 int *from_cache) /* in/out */
549 {
550 int needfree = 1, res, err;
551 struct __nsw_switchconfig *conf;
552 struct __nsw_lookup *look;
553 enum __nsw_parse_err perr;
554 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype);
555 int retry_cache = 0;
556
557 if (!netname || !pkey)
558 return (0);
559
560 conf = __nsw_getconfig("publickey", &perr);
561 if (!conf) {
562 conf = &publickey_default;
563 needfree = 0;
564 }
565 for (look = conf->lookups; look; look = look->next) {
566 if (strcmp(look->service_name, "ldap") == 0) {
567 res = getkeys_ldap_g(&err, (char *)netname,
568 pkey, pkeylen, NULL, 0, NULL,
569 keylen, algtype);
570 /* long DH keys will not be in nis or files */
571 } else if (classic_des &&
572 strcmp(look->service_name, "nis") == 0)
573 res = getkeys_nis(&err, (char *)netname, pkey,
574 NULL, NULL);
575 else if (classic_des &&
576 strcmp(look->service_name, "files") == 0)
577 res = getkeys_files(&err, (char *)netname, pkey,
578 NULL, NULL);
579 else {
580 syslog(LOG_INFO, "Unknown publickey nameservice '%s'",
581 look->service_name);
582 err = __NSW_UNAVAIL;
583 }
584
585 switch (look->actions[err]) {
586 case __NSW_CONTINUE :
587 continue;
588 case __NSW_RETURN :
589 if (needfree)
590 __nsw_freeconfig(conf);
591 return ((res & 1) != 0);
592 default :
593 syslog(LOG_INFO, "Unknown action for nameservice %s",
594 look->service_name);
595 }
596 }
597
598 if (needfree)
599 __nsw_freeconfig(conf);
600 return (0);
601 }
602
603
604
605 /*
606 * Generic (all sizes) DH version of getpublickey.
607 */
608 int
getpublickey_g(const char * netname,int keylen,int algtype,char * pkey,size_t pkeylen)609 getpublickey_g(
610 const char *netname, /* in */
611 int keylen, /* in */
612 int algtype, /* in */
613 char *pkey, /* out */
614 size_t pkeylen) /* in */
615 {
616 return (__getpublickey_cached_g(netname, keylen, algtype, pkey,
617 pkeylen, (int *)0));
618 }
619
620 /*
621 * Generic (all sizes) DH version of getsecretkey_g.
622 */
623 int
getsecretkey_g(const char * netname,keylen_t keylen,algtype_t algtype,char * skey,size_t skeylen,const char * passwd)624 getsecretkey_g(
625 const char *netname, /* in */
626 keylen_t keylen, /* in */
627 algtype_t algtype, /* in */
628 char *skey, /* out */
629 size_t skeylen, /* in */
630 const char *passwd) /* in */
631 {
632 int needfree = 1, res, err;
633 struct __nsw_switchconfig *conf;
634 struct __nsw_lookup *look;
635 enum __nsw_parse_err perr;
636 const bool_t classic_des = AUTH_DES_KEY(keylen, algtype);
637
638 if (!netname || !skey || !skeylen)
639 return (0);
640
641 conf = __nsw_getconfig("publickey", &perr);
642
643 if (!conf) {
644 conf = &publickey_default;
645 needfree = 0;
646 }
647
648 for (look = conf->lookups; look; look = look->next) {
649 if (strcmp(look->service_name, "ldap") == 0)
650 res = getkeys_ldap_g(&err, (char *)netname,
651 NULL, 0, skey, skeylen,
652 (char *)passwd, keylen, algtype);
653 /* long DH keys will not be in nis or files */
654 else if (classic_des && strcmp(look->service_name, "nis") == 0)
655 res = getkeys_nis(&err, (char *)netname,
656 NULL, skey, (char *)passwd);
657 else if (classic_des &&
658 strcmp(look->service_name, "files") == 0)
659 res = getkeys_files(&err, (char *)netname,
660 NULL, skey, (char *)passwd);
661 else {
662 syslog(LOG_INFO, "Unknown publickey nameservice '%s'",
663 look->service_name);
664 err = __NSW_UNAVAIL;
665 }
666 switch (look->actions[err]) {
667 case __NSW_CONTINUE :
668 continue;
669 case __NSW_RETURN :
670 if (needfree)
671 __nsw_freeconfig(conf);
672 return ((res & 2) != 0);
673 default :
674 syslog(LOG_INFO, "Unknown action for nameservice %s",
675 look->service_name);
676 }
677 }
678 if (needfree)
679 __nsw_freeconfig(conf);
680 return (0);
681 }
682