chiark / gitweb /
server/admin.c: Remove spurious `ping' in usage message.
[tripe] / server / keymgmt.c
1 /* -*-c-*-
2  *
3  * Key loading and storing
4  *
5  * (c) 2001 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of Trivial IP Encryption (TrIPE).
11  *
12  * TrIPE is free software: you can redistribute it and/or modify it under
13  * the terms of the GNU General Public License as published by the Free
14  * Software Foundation; either version 3 of the License, or (at your
15  * option) any later version.
16  *
17  * TrIPE is distributed in the hope that it will be useful, but WITHOUT
18  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20  * for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with TrIPE.  If not, see <https://www.gnu.org/licenses/>.
24  */
25
26 /*----- Header files ------------------------------------------------------*/
27
28 #include "tripe.h"
29
30 /*----- Algswitch stuff ---------------------------------------------------*/
31
32 /* --- @algs_get@ --- *
33  *
34  * Arguments:   @algswitch *a@ = where to put the algorithms
35  *              @dstr *e@ = where to write error tokens
36  *              @key_file *kf@ = key file
37  *              @key *k@ = key to inspect
38  *
39  * Returns:     Zero if OK; nonzero on error.
40  *
41  * Use:         Extracts an algorithm choice from a key.
42  */
43
44 static int algs_get(algswitch *a, dstr *e, key_file *kf, key *k)
45 {
46   const char *p;
47   const bulkops *bops;
48   dstr d = DSTR_INIT, dd = DSTR_INIT;
49   int rc = -1;
50
51   /* --- Hash function --- */
52
53   if ((p = key_getattr(kf, k, "hash")) == 0) p = "rmd160";
54   if ((a->h = ghash_byname(p)) == 0) {
55     a_format(e, "unknown-hash", "%s", p, A_END);
56     goto done;
57   }
58
59   /* --- Symmetric encryption for key derivation --- */
60
61   if ((p = key_getattr(kf, k, "mgf")) == 0) {
62     dstr_reset(&d);
63     dstr_putf(&d, "%s-mgf", a->h->name);
64     p = d.buf;
65   }
66   if ((a->mgf = gcipher_byname(p)) == 0) {
67     a_format(e, "unknown-mgf-cipher", "%s", p, A_END);
68     goto done;
69   }
70
71   /* --- Bulk crypto transform --- */
72
73   if ((p = key_getattr(kf, k, "bulk")) == 0) p = "v0";
74   for (bops = bulktab; bops->name && strcmp(p, bops->name) != 0; bops++);
75   if (!bops->name) {
76     a_format(e, "unknown-bulk-transform", "%s", p, A_END);
77     goto done;
78   }
79   if ((a->bulk = bops->getalgs(a, e, kf, k)) == 0) goto done;
80   a->bulk->ops = bops;
81
82   /* --- All done --- */
83
84   rc = 0;
85 done:
86   dstr_destroy(&d);
87   dstr_destroy(&dd);
88   return (rc);
89 }
90
91 /* --- @algs_check@ --- *
92  *
93  * Arguments:   @algswitch *a@ = a choice of algorithms
94  *              @dstr *e@ = where to write error tokens
95  *              @const dhgrp *grp@ = the group we're working in
96  *
97  * Returns:     Zero if OK; nonzero on error.
98  *
99  * Use:         Checks an algorithm choice for sensibleness.  This also
100  *              derives some useful information from the choices, and you
101  *              must call this before committing the algorithm selection
102  *              for use by @keyset@ functions.
103  */
104
105 static int algs_check(algswitch *a, dstr *e, const dhgrp *grp)
106 {
107   a->hashsz = a->h->hashsz;
108
109   if (keysz(a->hashsz, a->mgf->keysz) != a->hashsz) {
110     a_format(e, "mgf", "%s", a->mgf->name,
111              "restrictive-key-schedule",
112              A_END);
113     return (-1);
114   }
115
116   if (a->bulk->ops->checkalgs(a->bulk, a, e)) return (-1);
117
118   return (0);
119 }
120
121 /* --- @km_samealgsp@ --- *
122  *
123  * Arguments:   @const kdata *kdx, *kdy@ = two key data objects
124  *
125  * Returns:     Nonzero if their two algorithm selections are the same.
126  *
127  * Use:         Checks sameness of algorithm selections: used to ensure that
128  *              peers are using sensible algorithms.
129  */
130
131 int km_samealgsp(const kdata *kdx, const kdata *kdy)
132 {
133   const algswitch *a = &kdx->algs, *aa = &kdy->algs;
134
135   return (kdx->grp->ops == kdy->grp->ops &&
136           kdx->grp->ops->samegrpp(kdx->grp, kdy->grp) &&
137           a->mgf == aa->mgf && a->h == aa->h &&
138           a->bulk->ops == aa->bulk->ops &&
139           a->bulk->ops->samealgsp(a->bulk, aa->bulk));
140 }
141
142 /*----- Key data and key nodes --------------------------------------------*/
143
144 typedef struct keyhalf {
145   const char *kind;
146   int (*load)(key_file *, key *, key_data *,
147               const dhops *, kdata *, dstr *, dstr *);
148   char *kr;
149   key_file *kf;
150   fwatch w;
151   sym_table tab;
152 } keyhalf;
153
154 /* --- @kh_loadpub@, @kh_loadpriv@ --- *
155  *
156  * Arguments:   @const dhops *dh@ = Diffie--Hellman operations for key type
157  *              @key_file *kf@ = key file from which the key was loaded
158  *              @key *k@ = the key object we're loading
159  *              @key_data *d@ = the key data to load
160  *              @kdata *kd@ = our key-data object to fill in
161  *              @dstr *t@ = the key tag name
162  *              @dstr *e@ = a string to write error tokens to
163  *
164  * Returns:     Zero on success, @-1@ on error.
165  *
166  * Use:         These functions handle the main difference between public and
167  *              private key halves.  They are responsible for setting @grp@,
168  *              @k@ and @K@ appropriately in all keys, handling the mismatch
169  *              between the largely half-indifferent calling code and the
170  *              group-specific loading functions.
171  *
172  *              The function @kh_loadpriv@ is also responsible for checking
173  *              the group for goodness.  We don't bother checking public
174  *              keys, because each public key we actually end up using must
175  *              share a group with a private key which we'll already have
176  *              checked.
177  */
178
179 static int kh_loadpub(key_file *kf, key *k, key_data *d,
180                       const dhops *dh, kdata *kd, dstr *t, dstr *e)
181 {
182   int rc;
183
184   if ((rc = dh->ldpub(kf, k, d, kd, t, e)) != 0)
185     goto fail_0;
186   kd->grp->ops = dh;
187   if (kd->grp->ops->checkge(kd->grp, kd->K)) {
188     a_format(e, "bad-public-group-element", A_END);
189     goto fail_1;
190   }
191   return (0);
192
193 fail_1:
194   kd->grp->ops->freege(kd->grp, kd->K);
195   kd->grp->ops->freegrp(kd->grp);
196 fail_0:
197   return (-1);
198 }
199
200 static int kh_loadpriv(key_file *kf, key *k, key_data *d,
201                        const dhops *dh, kdata *kd, dstr *t, dstr *e)
202 {
203   int rc;
204   const char *err;
205   dhge *K;
206   int ok;
207
208   if ((rc = dh->ldpriv(kf, k, d, kd, t, e)) != 0)
209     goto fail_0;
210   kd->grp->ops = dh;
211   if ((err = kd->grp->ops->checkgrp(kd->grp)) != 0) {
212     a_format(e, "bad-group", "%s", err, A_END);
213     goto fail_1;
214   }
215   K = kd->grp->ops->mul(kd->grp, kd->k, 0);
216   ok = kd->grp->ops->eq(kd->grp, kd->K, K);
217   kd->grp->ops->freege(kd->grp, K);
218   if (!ok) {
219     a_format(e, "incorrect-public-key", A_END);
220     goto fail_1;
221   }
222   return (0);
223
224 fail_1:
225   kd->grp->ops->freesc(kd->grp, kd->k);
226   kd->grp->ops->freege(kd->grp, kd->K);
227   kd->grp->ops->freegrp(kd->grp);
228 fail_0:
229   return (-1);
230 }
231
232 static struct keyhalf
233   priv = { "private", kh_loadpriv },
234   pub = { "public", kh_loadpub };
235
236 /* --- @keymoan@ --- *
237  *
238  * Arguments:   @const char *file@ = name of the file
239  *              @int line@ = line number in file
240  *              @const char *msg@ = error message
241  *              @void *p@ = argument pointer (indicates which keyring)
242  *
243  * Returns:     ---
244  *
245  * Use:         Reports an error message about loading a key file.
246  */
247
248 static void keymoan(const char *file, int line, const char *msg, void *p)
249 {
250   keyhalf *kh = p;
251
252   if (!line) {
253     a_warn("KEYMGMT", "%s-keyring", kh->kind, "%s", file,
254            "io-error", "?ERRNO", A_END);
255   } else {
256     a_warn("KEYMGMT", "%s-keyring", kh->kind, "%s", file, "line", "%d", line,
257            "%s", msg, A_END);
258   }
259 }
260
261 /* --- @kh_reopen@ --- *
262  *
263  * Arguments:   @keyhalf *kh@ = pointer to keyhalf structure
264  *
265  * Returns:     Zero on success, @-1@ on error.
266  *
267  * Use:         Reopens the key file for the appropriate key half.  If this
268  *              fails, everything is left as it was; if it succeeds, then the
269  *              old file is closed (if it was non-null) and the new one put
270  *              in its place.
271  */
272
273 static int kh_reopen(keyhalf *kh)
274 {
275   key_file *kf = CREATE(key_file);
276
277   if (key_open(kf, kh->kr, KOPEN_READ, keymoan, kh)) {
278     DESTROY(kf);
279     return (-1);
280   }
281   if (kh->kf) {
282     key_close(kh->kf);
283     DESTROY(kh->kf);
284   }
285   kh->kf = kf;
286   return (0);
287 }
288
289 /* --- @kh_init@ --- *
290  *
291  * Arguments:   @keyhalf *kh@ = pointer to keyhalf structure to set up
292  *              @const char *kr@ = name of the keyring file
293  *
294  * Returns:     Zero on success, @-1@ on error.
295  *
296  * Use:         Initialize a keyhalf structure, maintaining the private or
297  *              public keys.  Intended to be called during initialization:
298  *              exits if there's some kind of problem.
299  */
300
301 static int kh_init(keyhalf *kh, const char *kr)
302 {
303   if (kh->kf) return (0);
304   kh->kr = xstrdup(kr);
305   if (kh_reopen(kh)) return (-1);
306   fwatch_init(&kh->w, kr);
307   sym_create(&kh->tab);
308   return (0);
309 }
310
311 /* --- @kh_load@ --- *
312  *
313  * Arguments:   @keyhalf *kh@ = pointer to keyhalf
314  *              @const char *tag@ = key tag to be loaded
315  *              @int complainp@ = whether to complain about missing keys
316  *
317  * Returns:     Pointer to a @kdata@ structure if successful, or null on
318  *              failure.
319  *
320  * Use:         Attempts to load a key from the current key file.  This
321  *              function always reads data from the file: it's used when
322  *              there's a cache miss from @kh_find@, and when refreshing the
323  *              known keys in @kh_refresh@.  The returned kdata has a
324  *              reference count of exactly 1, and has no home knode.
325  */
326
327 static kdata *kh_load(keyhalf *kh, const char *tag, int complainp)
328 {
329   dstr t = DSTR_INIT;
330   dstr e = DSTR_INIT;
331   key *k;
332   key_data **d;
333   kdata *kd;
334   const char *ty;
335   const dhops *dh;
336   T( const dhgrp *g; )
337
338   /* --- Find the key and grab its tag --- */
339
340   if (key_qtag(kh->kf, tag, &t, &k, &d)) {
341     if (complainp) {
342       a_warn("KEYMGMT", "%s-keyring", kh->kind, "%s", kh->kr,
343              "key-not-found", "%s", tag, A_END);
344     }
345     goto fail_0;
346   }
347
348   /* --- Find the key's group type and the appropriate operations --- *
349    *
350    * There are several places to look for the key type.  The most obvious is
351    * the `kx-group' key attribute.  But there's also the key type itself, for
352    * compatibility reasons.
353    */
354
355   ty = key_getattr(kh->kf, k, "kx-group");
356   if (!ty && strncmp(k->type, "tripe-", 6) == 0) ty = k->type + 6;
357   if (!ty) ty = "dh";
358
359   for (dh = dhtab; dh->name; dh++)
360     if (strcmp(dh->name, ty) == 0) goto founddh;
361   a_warn("KEYMGMT", "%s-keyring", kh->kind,
362          "%s", kh->kr, "key", "%s", t.buf,
363          "unknown-group-type", "%s", ty, A_END);
364   goto fail_0;
365
366 founddh:
367   kd = CREATE(kdata);
368   if (kh->load(kh->kf, k, *d, dh, kd, &t, &e)) {
369     a_warn("KEYMGMT", "%s-keyring", kh->kind,
370            "%s", kh->kr, "key", "%s", t.buf,
371            "*%s", e.buf, A_END);
372     goto fail_1;
373   }
374
375   if (algs_get(&kd->algs, &e, kh->kf, k) ||
376       algs_check(&kd->algs, &e, kd->grp)) {
377     a_warn("KEYMGMT", "%s-keyring", kh->kind,
378            "%s", kh->kr, "key", "%s", t.buf,
379            "*%s", e.buf, A_END);
380     goto fail_2;
381   }
382
383   kd->tag = xstrdup(t.buf);
384   kd->ref = 1;
385   kd->kn = 0;
386   kd->id = k->id;
387   kd->t_exp = k->exp;
388
389   IF_TRACING(T_KEYMGMT, {
390     trace(T_KEYMGMT, "keymgmt: loaded %s key `%s'", kh->kind, t.buf);
391     IF_TRACING(T_CRYPTO, {
392       g = kd->grp;
393       g->ops->tracegrp(g);
394       if (kd->k)
395         trace(T_CRYPTO, "crypto: k = %s", g->ops->scstr(g, kd->k));
396       trace(T_CRYPTO, "crypto: K = %s", g->ops->gestr(g, kd->K));
397       trace(T_CRYPTO, "crypto: bulk transform = %s",
398             kd->algs.bulk->ops->name);
399       kd->algs.bulk->ops->tracealgs(kd->algs.bulk);
400     })
401   })
402
403   goto done;
404
405 fail_2:
406   if (kd->k) kd->grp->ops->freesc(kd->grp, kd->k);
407   kd->grp->ops->freege(kd->grp, kd->K);
408   kd->grp->ops->freegrp(kd->grp);
409 fail_1:
410   DESTROY(kd);
411 fail_0:
412   kd = 0;
413 done:
414   dstr_destroy(&t);
415   dstr_destroy(&e);
416   return (kd);
417 }
418
419 /* --- @kh_find@ --- *
420  *
421  * Arguments:   @keyhalf *kh@ = pointer to the keyhalf
422  *              @const char *tag@ = key to be obtained
423  *              @int complainp@ = whether to complain about missing keys
424  *
425  * Returns:     A pointer to the kdata, or null on error.
426  *
427  * Use:         Obtains kdata, maybe from the cache.  This won't update a
428  *              stale cache entry, though @kh_refresh@ ought to have done
429  *              that already.  The returned kdata object may be shared with
430  *              other users.  (One of this function's responsibilities, over
431  *              @kh_load@, is to set the home knode of a freshly loaded
432  *              kdata.)
433  */
434
435 static kdata *kh_find(keyhalf *kh, const char *tag, int complainp)
436 {
437   knode *kn;
438   kdata *kd;
439   unsigned f;
440
441   kn = sym_find(&kh->tab, tag, -1, sizeof(knode), &f);
442
443   if (f) {
444     if (kn->f & KNF_BROKEN) {
445       T( if (complainp)
446            trace(T_KEYMGMT, "keymgmt: key `%s' marked as broken", tag); )
447       return (0);
448     }
449
450     kd = kn->kd;
451     if (kd) kd->ref++;
452     T( trace(T_KEYMGMT, "keymgmt: %scache hit for key `%s'",
453              kd ? "" : "negative ", tag); )
454     return (kd);
455   } else {
456     kd = kh_load(kh, tag, complainp);
457     kn->kd = kd;
458     kn->kh = kh;
459     kn->f = 0;
460     if (!kd)
461       kn->f |= KNF_BROKEN;
462     else {
463       kd->kn = kn;
464       kd->ref++;
465     }
466     return (kd);
467   }
468 }
469
470 /* --- @kh_refresh@ --- *
471  *
472  * Arguments:   @keyhalf *kh@ = pointer to the keyhalf
473  *
474  * Returns:     Zero if nothing needs to be done; nonzero if peers should
475  *              refresh their keys.
476  *
477  * Use:         Refreshes cached keys from files.
478  *
479  *              Each active knode is examined to see if a new key is
480  *              available: the return value is nonzero if any new keys are.
481  *              A key is considered new if its algorithms, public key, or
482  *              expiry time are/is different.
483  *
484  *              Stub knodes (with no kdata attached) are removed, so that a
485  *              later retry can succeed if the file has been fixed.  (This
486  *              doesn't count as a change, since no peers should be relying
487  *              on a nonexistent key.)
488  */
489
490 static int kh_refresh(keyhalf *kh)
491 {
492   knode *kn;
493   kdata *kd;
494   sym_iter i;
495   int changep = 0;
496
497   if (!fwatch_update(&kh->w, kh->kr) || kh_reopen(kh))
498     return (0);
499
500   T( trace(T_KEYMGMT, "keymgmt: rescan %s keyring `%s'", kh->kind, kh->kr); )
501   for (sym_mkiter(&i, &kh->tab); (kn = sym_next(&i)) != 0; ) {
502     if (!kn->kd) {
503       T( trace(T_KEYMGMT, "keymgmt: discard stub entry for key `%s'",
504                SYM_NAME(kn)); )
505       sym_remove(&kh->tab, kn);
506       continue;
507     }
508     if ((kd = kh_load(kh, SYM_NAME(kn), 1)) == 0) {
509       if (!(kn->f & KNF_BROKEN)) {
510         T( trace(T_KEYMGMT, "keymgmt: failed to load new key `%s': "
511                  "marking it as broken",
512                  SYM_NAME(kn)); )
513         kn->f |= KNF_BROKEN;
514       }
515       continue;
516     }
517     kn->f &= ~KNF_BROKEN;
518     if (kd->t_exp == kn->kd->t_exp &&
519         km_samealgsp(kd, kn->kd) &&
520         kd->grp->ops->eq(kd->grp, kd->K, kn->kd->K)) {
521       T( trace(T_KEYMGMT, "keymgmt: key `%s' unchanged", SYM_NAME(kn)); )
522       continue;
523     }
524     T( trace(T_KEYMGMT, "keymgmt: loaded new version of key `%s'",
525              SYM_NAME(kn)); )
526     km_unref(kn->kd);
527     kd->kn = kn;
528     kn->kd = kd;
529     changep = 1;
530   }
531
532   return (changep);
533 }
534
535 /* --- @kh_clear@ --- *
536  *
537  * Arguments:   @keyhalf *kh@ = pointer to keyhalf structure
538  *
539  * Returns:     ---
540  *
541  * Use:         Clears out the keyhalf's keyring and flushes the cache.
542  */
543
544 static void kh_clear(keyhalf *kh)
545 {
546   sym_iter i;
547   knode *kn;
548
549   if (!kh->kf) return;
550   for (sym_mkiter(&i, &kh->tab); (kn = sym_next(&i)) != 0; )
551     if (kn->kd) km_unref(kn->kd);
552   sym_destroy(&kh->tab);
553   key_close(kh->kf);
554   xfree(kh->kr);
555   kh->kf = 0;
556 }
557
558 /*----- Main code ---------------------------------------------------------*/
559
560 char *tag_priv = 0;
561 kdata *master = 0;
562
563 /* --- @km_init@ --- *
564  *
565  * Arguments:   @const char *privkr@ = private keyring file
566  *              @const char *pubkr@ = public keyring file
567  *              @const char *ptag@ = default private-key tag
568  *
569  * Returns:     Zero on success, @-1@ on failure.
570  *
571  * Use:         Initializes the key-management machinery, loading the
572  *              keyrings and so on.
573  */
574
575 int km_init(const char *privkr, const char *pubkr, const char *ptag)
576 {
577   const gchash *const *hh;
578   kdata *kd;
579
580   for (hh = ghashtab; *hh; hh++) {
581     if ((*hh)->hashsz > MAXHASHSZ) {
582       a_warn("ABORT", "hash-size-too-large", "hash",
583              "%s", (*hh)->name, "size", "%lu", (*hh)->hashsz,
584              "limit", "%d", MAXHASHSZ, A_END);
585       abort();
586     }
587   }
588
589   if (kh_init(&priv, privkr) || kh_init(&pub, pubkr))
590     return (-1);
591
592   tag_priv = ptag ? xstrdup(ptag) : 0;
593   kh_refresh(&priv);
594
595   if ((kd = km_findpriv(tag_priv)) == 0) return (-1);
596   if (master) km_unref(master);
597   master = kd;
598
599   return (0);
600 }
601
602 /* --- @km_reload@ --- *
603  *
604  * Arguments:   ---
605  *
606  * Returns:     Zero if OK, nonzero to force reloading of keys.
607  *
608  * Use:         Checks the keyrings to see if they need reloading.
609  */
610
611 int km_reload(void)
612 {
613   int changep = 0;
614   kdata *kd;
615
616   if (kh_refresh(&priv)) {
617     changep = 1;
618     kd = master->kn->kd;
619     if (kd != master) {
620       km_unref(master);
621       km_ref(kd);
622       master = kd;
623     }
624   }
625   if (kh_refresh(&pub))
626     changep = 1;
627   return (changep);
628 }
629
630 /* --- @km_clear@ --- *
631  *
632  * Arguments:   ---
633  *
634  * Returns:     ---
635  *
636  * Use:         Forget the currently loaded keyrings.  The @master@ key will
637  *              be cleared, but other keys already loaded will continue to
638  *              exist until their reference count drops to zero.  Call
639  *              @km_init@ to make everything work again.
640  */
641
642 void km_clear(void)
643 {
644   kh_clear(&priv);
645   kh_clear(&pub);
646   if (master) { km_unref(master); master = 0; }
647   if (tag_priv) { xfree(tag_priv); tag_priv = 0; }
648 }
649
650 /* --- @km_findpub@, @km_findpriv@ --- *
651  *
652  * Arguments:   @const char *tag@ = key tag to load
653  *
654  * Returns:     Pointer to the kdata object if successful, or null on error.
655  *
656  * Use:         Fetches a public or private key from the keyring.
657  */
658
659 kdata *km_findpub(const char *tag) { return (kh_find(&pub, tag, 1)); }
660
661 kdata *km_findpriv(const char *tag)
662 {
663   kdata *kd;
664
665   /* Unpleasantness for the sake of compatibility. */
666   if (!tag && (kd = kh_find(&priv, "tripe", 0)) != 0) return (kd);
667   else return (kh_find(&priv, tag ? tag : "tripe-dh", 1));
668 }
669
670 /* --- @km_findpubbyid@, @km_findprivbyid@ --- *
671  *
672  * Arguments:   @uint32 id@ = key id to load
673  *
674  * Returns:     Pointer to the kdata object if successful, or null on error.
675  *
676  * Use:         Fetches a public or private key from the keyring given its
677  *              numeric id.
678  */
679
680 static kdata *findbyid(keyhalf *kh, uint32 id)
681 {
682   key *k;
683   kdata *kd;
684
685   k = key_byid(kh->kf, id); if (!k) goto notfound;
686   kd = kh_find(kh, k->tag, 1); if (!kd) goto notfound;
687   if (kd->id != id) { km_unref(kd); goto notfound; }
688   return (kd);
689
690 notfound:
691   a_warn("KX", "%s-keyring", kh->kind, "%s", kh->kr,
692          "unknown-key-id", "0x%08lx", (unsigned long)id,
693          A_END);
694   return (0);
695 }
696
697 kdata *km_findpubbyid(uint32 id) { return (findbyid(&pub, id)); }
698
699 kdata *km_findprivbyid(uint32 id)
700 {
701   if (id == master->id) { km_ref(master); return (master); }
702   else return findbyid(&priv, id);
703 }
704
705 /* --- @km_tag@ --- *
706  *
707  * Arguments:   @kdata *kd@ - pointer to the kdata object
708  *
709  * Returns:     A pointer to the short tag by which the kdata was loaded.
710  */
711
712 const char *km_tag(kdata *kd) { return (SYM_NAME(kd->kn)); }
713
714 /* --- @km_ref@ --- *
715  *
716  * Arguments:   @kdata *kd@ = pointer to the kdata object
717  *
718  * Returns:     ---
719  *
720  * Use:         Claim a new reference to a kdata object.
721  */
722
723 void km_ref(kdata *kd) { kd->ref++; }
724
725 /* --- @km_unref@ --- *
726  *
727  * Arguments:   @kdata *kd@ = pointer to the kdata object
728  *
729  * Returns:     ---
730  *
731  * Use:         Releases a reference to a kdata object.
732  */
733
734 void km_unref(kdata *kd)
735 {
736   if (--kd->ref) return;
737   if (kd->k) kd->grp->ops->freesc(kd->grp, kd->k);
738   kd->grp->ops->freege(kd->grp, kd->K);
739   kd->grp->ops->freegrp(kd->grp);
740   xfree(kd->tag);
741   DESTROY(kd);
742 }
743
744 /*----- That's all, folks -------------------------------------------------*/