Removed deprecated SIGEXPIRED status line.
[gnupg.git] / g10 / sig-check.c
1 /* sig-check.c -  Check a signature
2  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003,
3  *               2004, 2006 Free Software Foundation, Inc.
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <assert.h>
26
27 #include "gpg.h"
28 #include "util.h"
29 #include "packet.h"
30 #include "keydb.h"
31 #include "cipher.h"
32 #include "main.h"
33 #include "status.h"
34 #include "i18n.h"
35 #include "options.h"
36 #include "pkglue.h"
37
38 /* Context used by the compare function. */
39 struct cmp_help_context_s
40 {
41   PKT_signature *sig;
42   gcry_md_hd_t md;
43 };
44
45
46
47 static int do_check( PKT_public_key *pk, PKT_signature *sig,
48                      gcry_md_hd_t digest,
49                      int *r_expired, int *r_revoked, PKT_public_key *ret_pk);
50
51 /****************
52  * Check the signature which is contained in SIG.
53  * The MD_HANDLE should be currently open, so that this function
54  * is able to append some data, before finalizing the digest.
55  */
56 int
57 signature_check (PKT_signature *sig, gcry_md_hd_t digest)
58 {
59     return signature_check2( sig, digest, NULL, NULL, NULL, NULL );
60 }
61
62 int
63 signature_check2 (PKT_signature *sig, gcry_md_hd_t digest, u32 *r_expiredate, 
64                   int *r_expired, int *r_revoked, PKT_public_key *ret_pk )
65 {
66     PKT_public_key *pk = xmalloc_clear( sizeof *pk );
67     int rc=0;
68
69     if ( (rc=openpgp_md_test_algo(sig->digest_algo)) )
70       ; /* We don't have this digest. */
71     else if ((rc=openpgp_pk_test_algo(sig->pubkey_algo)))
72       ; /* We don't have this pubkey algo. */
73     else if (!gcry_md_is_enabled (digest,sig->digest_algo))
74       {
75         /* Sanity check that the md has a context for the hash that the
76            sig is expecting.  This can happen if a onepass sig header does
77            not match the actual sig, and also if the clearsign "Hash:"
78            header is missing or does not match the actual sig. */
79
80         log_info(_("WARNING: signature digest conflict in message\n"));
81         rc=G10ERR_GENERAL;
82       }
83     else if( get_pubkey( pk, sig->keyid ) )
84         rc = G10ERR_NO_PUBKEY;
85     else if(!pk->flags.valid && !pk->flags.primary)
86         rc=G10ERR_BAD_PUBKEY; /* you cannot have a good sig from an
87                                  invalid subkey */
88     else
89       {
90         if(r_expiredate)
91           *r_expiredate = pk->expiredate;
92
93         rc = do_check( pk, sig, digest, r_expired, r_revoked, ret_pk );
94
95         /* Check the backsig.  This is a 0x19 signature from the
96            subkey on the primary key.  The idea here is that it should
97            not be possible for someone to "steal" subkeys and claim
98            them as their own.  The attacker couldn't actually use the
99            subkey, but they could try and claim ownership of any
100            signaures issued by it. */
101         if(rc==0 && !pk->flags.primary && pk->flags.backsig < 2)
102           {
103             if (!pk->flags.backsig)
104               {
105                 log_info(_("WARNING: signing subkey %s is not"
106                            " cross-certified\n"),keystr_from_pk(pk));
107                 log_info(_("please see %s for more information\n"),
108                          "http://www.gnupg.org/faq/subkey-cross-certify.html");
109                 /* --require-cross-certification makes this warning an
110                      error.  TODO: change the default to require this
111                      after more keys have backsigs. */
112                 if(opt.flags.require_cross_cert)
113                   rc=G10ERR_GENERAL;
114               }
115             else if(pk->flags.backsig == 1)
116               {
117                 log_info(_("WARNING: signing subkey %s has an invalid"
118                            " cross-certification\n"),keystr_from_pk(pk));
119                 rc=G10ERR_GENERAL;
120               }
121           }
122       }
123
124     free_public_key( pk );
125
126     if( !rc && sig->sig_class < 2 && is_status_enabled() ) {
127         /* This signature id works best with DLP algorithms because
128          * they use a random parameter for every signature.  Instead of
129          * this sig-id we could have also used the hash of the document
130          * and the timestamp, but the drawback of this is, that it is
131          * not possible to sign more than one identical document within
132          * one second.  Some remote batch processing applications might
133          * like this feature here.  
134          * 
135          * Note that before 2.0.10, we used RIPE-MD160 for the hash
136          * and accidently didn't include the timestamp and algorithm
137          * information in the hash.  Given that this feature is not
138          * commonly used and that a replay attacks detection should
139          * not solely be based on this feature (because it does not
140          * work with RSA), we take the freedom and switch to SHA-1
141          * with 2.0.10 to take advantage of hardware supported SHA-1
142          * implementations.  We also include the missing information
143          * in the hash.  Note also the SIG_ID as computed by gpg 1.x
144          * and gpg 2.x didn't matched either because 2.x used to print
145          * MPIs not in PGP format.  */
146         u32 a = sig->timestamp;
147         int nsig = pubkey_get_nsig( sig->pubkey_algo );
148         unsigned char *p, *buffer;
149         size_t n, nbytes;
150         int i;
151         char hashbuf[20];
152
153         nbytes = 6;
154         for (i=0; i < nsig; i++ )
155           {
156             if (gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, &n, sig->data[i]))
157               BUG();
158             nbytes += n;
159           }
160
161         /* Make buffer large enough to be later used as output buffer.  */
162         if (nbytes < 100)
163           nbytes = 100;
164         nbytes += 10;  /* Safety margin.  */
165
166         /* Fill and hash buffer.  */
167         buffer = p = xmalloc (nbytes);
168         *p++ = sig->pubkey_algo;
169         *p++ = sig->digest_algo;
170         *p++ = (a >> 24) & 0xff;
171         *p++ = (a >> 16) & 0xff;
172         *p++ = (a >>  8) & 0xff;
173         *p++ =  a & 0xff;
174         nbytes -= 6;
175         for (i=0; i < nsig; i++ )
176           {
177             if (gcry_mpi_print (GCRYMPI_FMT_PGP, p, nbytes, &n, sig->data[i]))
178               BUG();
179             p += n;
180             nbytes -= n;
181           }
182         gcry_md_hash_buffer (GCRY_MD_SHA1, hashbuf, buffer, p-buffer);
183
184         p = make_radix64_string (hashbuf, 20);
185         sprintf (buffer, "%s %s %lu",
186                  p, strtimestamp (sig->timestamp), (ulong)sig->timestamp);
187         xfree (p);
188         write_status_text (STATUS_SIG_ID, buffer);
189         xfree (buffer);
190     }
191
192     return rc;
193 }
194
195
196 static int
197 do_check_messages( PKT_public_key *pk, PKT_signature *sig,
198                    int *r_expired, int *r_revoked )
199 {
200     u32 cur_time;
201
202     if(r_expired)
203       *r_expired = 0;
204     if(r_revoked)
205       *r_revoked = 0;
206
207     if( pk->timestamp > sig->timestamp )
208       {
209         ulong d = pk->timestamp - sig->timestamp;
210         log_info(d==1
211                  ?_("public key %s is %lu second newer than the signature\n")
212                  :_("public key %s is %lu seconds newer than the signature\n"),
213                  keystr_from_pk(pk),d );
214         if( !opt.ignore_time_conflict )
215           return G10ERR_TIME_CONFLICT; /* pubkey newer than signature */
216       }
217
218     cur_time = make_timestamp();
219     if( pk->timestamp > cur_time )
220       {
221         ulong d = pk->timestamp - cur_time;
222         log_info( d==1
223                   ? _("key %s was created %lu second"
224                       " in the future (time warp or clock problem)\n")
225                   : _("key %s was created %lu seconds"
226                       " in the future (time warp or clock problem)\n"),
227                   keystr_from_pk(pk),d );
228         if( !opt.ignore_time_conflict )
229           return G10ERR_TIME_CONFLICT;
230       }
231
232     /* Check whether the key has expired.  We check the has_expired
233        flag which is set after a full evaluation of the key (getkey.c)
234        as well as a simple compare to the current time in case the
235        merge has for whatever reasons not been done.  */
236     if( pk->has_expired || (pk->expiredate && pk->expiredate < cur_time)) {
237         char buf[11];
238         if (opt.verbose)
239           log_info(_("NOTE: signature key %s expired %s\n"),
240                    keystr_from_pk(pk), asctimestamp( pk->expiredate ) );
241         sprintf(buf,"%lu",(ulong)pk->expiredate);
242         write_status_text(STATUS_KEYEXPIRED,buf);
243         if(r_expired)
244           *r_expired = 1;
245     }
246
247     if (pk->flags.revoked)
248       {
249         if (opt.verbose)
250           log_info (_("NOTE: signature key %s has been revoked\n"),
251                     keystr_from_pk(pk));
252         if (r_revoked)
253           *r_revoked=1;
254       }
255
256     return 0;
257 }
258
259
260 static int
261 do_check( PKT_public_key *pk, PKT_signature *sig, gcry_md_hd_t digest,
262           int *r_expired, int *r_revoked, PKT_public_key *ret_pk )
263 {
264     gcry_mpi_t result = NULL;
265     int rc = 0;
266     struct cmp_help_context_s ctx;
267
268     if( (rc=do_check_messages(pk,sig,r_expired,r_revoked)) )
269         return rc;
270
271     /* Make sure the digest algo is enabled (in case of a detached
272        signature).  */
273     gcry_md_enable (digest, sig->digest_algo);
274
275     /* Complete the digest. */
276     if( sig->version >= 4 )
277         gcry_md_putc( digest, sig->version );
278     gcry_md_putc( digest, sig->sig_class );
279     if( sig->version < 4 ) {
280         u32 a = sig->timestamp;
281         gcry_md_putc( digest, (a >> 24) & 0xff );
282         gcry_md_putc( digest, (a >> 16) & 0xff );
283         gcry_md_putc( digest, (a >>     8) & 0xff );
284         gcry_md_putc( digest,  a           & 0xff );
285     }
286     else {
287         byte buf[6];
288         size_t n;
289         gcry_md_putc( digest, sig->pubkey_algo );
290         gcry_md_putc( digest, sig->digest_algo );
291         if( sig->hashed ) {
292             n = sig->hashed->len;
293             gcry_md_putc (digest, (n >> 8) );
294             gcry_md_putc (digest,  n       );
295             gcry_md_write (digest, sig->hashed->data, n);
296             n += 6;
297         }
298         else {
299           /* Two octets for the (empty) length of the hashed
300              section. */
301           gcry_md_putc (digest, 0);
302           gcry_md_putc (digest, 0);
303           n = 6;
304         }
305         /* add some magic */
306         buf[0] = sig->version;
307         buf[1] = 0xff;
308         buf[2] = n >> 24;
309         buf[3] = n >> 16;
310         buf[4] = n >>  8;
311         buf[5] = n;
312         gcry_md_write( digest, buf, 6 );
313     }
314     gcry_md_final( digest );
315
316     result = encode_md_value (pk, digest, sig->digest_algo );
317     if (!result)
318         return G10ERR_GENERAL;
319     ctx.sig = sig;
320     ctx.md = digest;
321     rc = pk_verify( pk->pubkey_algo, result, sig->data, pk->pkey );
322     gcry_mpi_release (result);
323
324     if( !rc && sig->flags.unknown_critical )
325       {
326         log_info(_("assuming bad signature from key %s"
327                    " due to an unknown critical bit\n"),keystr_from_pk(pk));
328         rc = G10ERR_BAD_SIGN;
329       }
330
331     if(!rc && ret_pk)
332       copy_public_key(ret_pk,pk);
333
334     return rc;
335 }
336
337
338
339 static void
340 hash_uid_node( KBNODE unode, gcry_md_hd_t md, PKT_signature *sig )
341 {
342     PKT_user_id *uid = unode->pkt->pkt.user_id;
343
344     assert( unode->pkt->pkttype == PKT_USER_ID );
345     if( uid->attrib_data ) {
346         if( sig->version >=4 ) {
347             byte buf[5];
348             buf[0] = 0xd1;                   /* packet of type 17 */
349             buf[1] = uid->attrib_len >> 24;  /* always use 4 length bytes */
350             buf[2] = uid->attrib_len >> 16;
351             buf[3] = uid->attrib_len >>  8;
352             buf[4] = uid->attrib_len;
353             gcry_md_write( md, buf, 5 );
354         }
355         gcry_md_write( md, uid->attrib_data, uid->attrib_len );
356     }
357     else {
358         if( sig->version >=4 ) {
359             byte buf[5];
360             buf[0] = 0xb4;            /* indicates a userid packet */
361             buf[1] = uid->len >> 24;  /* always use 4 length bytes */
362             buf[2] = uid->len >> 16;
363             buf[3] = uid->len >>  8;
364             buf[4] = uid->len;
365             gcry_md_write( md, buf, 5 );
366         }
367         gcry_md_write( md, uid->name, uid->len );
368     }
369 }
370
371 static void
372 cache_sig_result ( PKT_signature *sig, int result )
373 {
374     if ( !result ) {
375         sig->flags.checked = 1;
376         sig->flags.valid = 1;
377     }
378     else if ( gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE ) {
379         sig->flags.checked = 1;
380         sig->flags.valid = 0;
381     }
382     else {
383         sig->flags.checked = 0;
384         sig->flags.valid = 0;
385     }
386 }
387
388 /* Check the revocation keys to see if any of them have revoked our
389    pk.  sig is the revocation sig.  pk is the key it is on.  This code
390    will need to be modified if gpg ever becomes multi-threaded.  Note
391    that this guarantees that a designated revocation sig will never be
392    considered valid unless it is actually valid, as well as being
393    issued by a revocation key in a valid direct signature.  Note also
394    that this is written so that a revoked revoker can still issue
395    revocations: i.e. If A revokes B, but A is revoked, B is still
396    revoked.  I'm not completely convinced this is the proper behavior,
397    but it matches how PGP does it. -dms */
398
399 /* Returns 0 if sig is valid (i.e. pk is revoked), non-0 if not
400    revoked.  It is important that G10ERR_NO_PUBKEY is only returned
401    when a revocation signature is from a valid revocation key
402    designated in a revkey subpacket, but the revocation key itself
403    isn't present. */
404 int
405 check_revocation_keys(PKT_public_key *pk,PKT_signature *sig)
406 {
407   static int busy=0;
408   int i,rc=G10ERR_GENERAL;
409
410   assert(IS_KEY_REV(sig));
411   assert((sig->keyid[0]!=pk->keyid[0]) || (sig->keyid[0]!=pk->keyid[1]));
412
413   if (busy)
414     {
415       /* Return an error (i.e. not revoked), but mark the pk as
416          uncacheable as we don't really know its revocation status
417          until it is checked directly.  */
418       pk->flags.dont_cache = 1;
419       return rc;
420     }
421
422   busy=1;
423
424   /*  printf("looking at %08lX with a sig from %08lX\n",(ulong)pk->keyid[1],
425       (ulong)sig->keyid[1]); */
426
427   /* is the issuer of the sig one of our revokers? */
428   if( !pk->revkey && pk->numrevkeys )
429      BUG();
430   else
431       for(i=0;i<pk->numrevkeys;i++)
432         {
433           u32 keyid[2];
434     
435           keyid_from_fingerprint(pk->revkey[i].fpr,MAX_FINGERPRINT_LEN,keyid);
436     
437           if(keyid[0]==sig->keyid[0] && keyid[1]==sig->keyid[1])
438             {
439               gcry_md_hd_t md;
440     
441               if (gcry_md_open (&md, sig->digest_algo, 0))
442                 BUG ();
443               hash_public_key(md,pk);
444               rc=signature_check(sig,md);
445               cache_sig_result(sig,rc);
446               gcry_md_close (md);
447               break;
448             }
449         }
450
451   busy=0;
452
453   return rc;
454
455
456 /* Backsigs (0x19) have the same format as binding sigs (0x18), but
457    this function is simpler than check_key_signature in a few ways.
458    For example, there is no support for expiring backsigs since it is
459    questionable what such a thing actually means.  Note also that the
460    sig cache check here, unlike other sig caches in GnuPG, is not
461    persistent. */
462 int
463 check_backsig(PKT_public_key *main_pk,PKT_public_key *sub_pk,
464               PKT_signature *backsig)
465 {
466   gcry_md_hd_t md;
467   int rc;
468
469   /* Always check whether the algorithm is available.  Although
470      gcry_md_open woyuld throw an error, some libgcrypt versions will
471      print a debug message in that case too. */
472   if ((rc=openpgp_md_test_algo (backsig->digest_algo)))
473     return rc;
474
475   if(!opt.no_sig_cache && backsig->flags.checked)
476     return backsig->flags.valid? 0 : gpg_error (GPG_ERR_BAD_SIGNATURE);
477
478   rc = gcry_md_open (&md, backsig->digest_algo,0);
479   if (!rc)
480     {
481       hash_public_key(md,main_pk);
482       hash_public_key(md,sub_pk);
483       rc=do_check(sub_pk,backsig,md,NULL,NULL,NULL);
484       cache_sig_result(backsig,rc);
485       gcry_md_close(md);
486     }
487
488   return rc;
489 }
490
491
492 /****************
493  * check the signature pointed to by NODE. This is a key signature.
494  * If the function detects a self-signature, it uses the PK from
495  * ROOT and does not read any public key.
496  */
497 int
498 check_key_signature( KBNODE root, KBNODE node, int *is_selfsig )
499 {
500   return check_key_signature2(root, node, NULL, NULL, is_selfsig, NULL, NULL );
501 }
502
503 /* If check_pk is set, then use it to check the signature in node
504    rather than getting it from root or the keydb.  If ret_pk is set,
505    fill in the public key that was used to verify the signature.
506    ret_pk is only meaningful when the verification was successful. */
507 /* TODO: add r_revoked here as well.  It has the same problems as
508    r_expiredate and r_expired and the cache. */
509 int
510 check_key_signature2( KBNODE root, KBNODE node, PKT_public_key *check_pk,
511                       PKT_public_key *ret_pk, int *is_selfsig,
512                       u32 *r_expiredate, int *r_expired )
513 {
514     gcry_md_hd_t md;
515     PKT_public_key *pk;
516     PKT_signature *sig;
517     int algo;
518     int rc;
519
520     if( is_selfsig )
521         *is_selfsig = 0;
522     if( r_expiredate )
523         *r_expiredate = 0;
524     if( r_expired )
525         *r_expired = 0;
526     assert( node->pkt->pkttype == PKT_SIGNATURE );
527     assert( root->pkt->pkttype == PKT_PUBLIC_KEY );
528
529     pk = root->pkt->pkt.public_key;
530     sig = node->pkt->pkt.signature;
531     algo = sig->digest_algo;
532
533     /* Check whether we have cached the result of a previous signature
534        check.  Note that we may no longer have the pubkey or hash
535        needed to verify a sig, but can still use the cached value.  A
536        cache refresh detects and clears these cases. */
537     if ( !opt.no_sig_cache ) {
538         if (sig->flags.checked) { /*cached status available*/
539             if( is_selfsig ) {  
540                 u32 keyid[2];   
541
542                 keyid_from_pk( pk, keyid );
543                 if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] )
544                     *is_selfsig = 1;
545             }
546             /* BUG: This is wrong for non-self-sigs.. needs to be the
547                actual pk */
548             if((rc=do_check_messages(pk,sig,r_expired,NULL)))
549               return rc;
550             return sig->flags.valid? 0 : gpg_error (GPG_ERR_BAD_SIGNATURE);
551         }
552     }
553
554     if( (rc=openpgp_pk_test_algo(sig->pubkey_algo)) )
555         return rc;
556     if( (rc=openpgp_md_test_algo(algo)) )
557         return rc;
558
559     if( sig->sig_class == 0x20 ) { /* key revocation */
560         u32 keyid[2];   
561         keyid_from_pk( pk, keyid );
562
563         /* is it a designated revoker? */
564         if(keyid[0]!=sig->keyid[0] || keyid[1]!=sig->keyid[1])
565           rc=check_revocation_keys(pk,sig);
566         else
567           {
568             if (gcry_md_open (&md, algo, 0 ))
569               BUG ();
570             hash_public_key( md, pk );
571             rc = do_check( pk, sig, md, r_expired, NULL, ret_pk );
572             cache_sig_result ( sig, rc );
573             gcry_md_close(md);
574           }
575     }
576     else if( sig->sig_class == 0x28 ) { /* subkey revocation */
577         KBNODE snode = find_prev_kbnode( root, node, PKT_PUBLIC_SUBKEY );
578
579         if( snode ) {
580             if (gcry_md_open (&md, algo, 0))
581               BUG ();
582             hash_public_key( md, pk );
583             hash_public_key( md, snode->pkt->pkt.public_key );
584             rc = do_check( pk, sig, md, r_expired, NULL, ret_pk );
585             cache_sig_result ( sig, rc );
586             gcry_md_close(md);
587         }
588         else
589           {
590             if (opt.verbose)
591               log_info (_("key %s: no subkey for subkey"
592                           " revocation signature\n"),keystr_from_pk(pk));
593             rc = G10ERR_SIG_CLASS;
594           }
595     }
596     else if( sig->sig_class == 0x18 ) { /* key binding */
597         KBNODE snode = find_prev_kbnode( root, node, PKT_PUBLIC_SUBKEY );
598
599         if( snode ) {
600             if( is_selfsig ) {  /* does this make sense????? */
601                 u32 keyid[2];   /* it should always be a selfsig */
602
603                 keyid_from_pk( pk, keyid );
604                 if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] )
605                     *is_selfsig = 1;
606             }
607             if (gcry_md_open (&md, algo, 0))
608               BUG ();
609             hash_public_key( md, pk );
610             hash_public_key( md, snode->pkt->pkt.public_key );
611             rc = do_check( pk, sig, md, r_expired, NULL, ret_pk );
612             cache_sig_result ( sig, rc );
613             gcry_md_close(md);
614         }
615         else
616           {
617             if (opt.verbose)
618               log_info(_("key %s: no subkey for subkey"
619                          " binding signature\n"),keystr_from_pk(pk));
620             rc = G10ERR_SIG_CLASS;
621           }
622     }
623     else if( sig->sig_class == 0x1f ) { /* direct key signature */
624         if (gcry_md_open (&md, algo, 0 ))
625           BUG ();
626         hash_public_key( md, pk );
627         rc = do_check( pk, sig, md, r_expired, NULL, ret_pk );
628         cache_sig_result ( sig, rc );
629         gcry_md_close(md);
630     }
631     else { /* all other classes */
632         KBNODE unode = find_prev_kbnode( root, node, PKT_USER_ID );
633
634         if( unode ) {
635             u32 keyid[2];
636
637             keyid_from_pk( pk, keyid );
638             if (gcry_md_open (&md, algo, 0 ))
639               BUG ();
640             hash_public_key( md, pk );
641             hash_uid_node( unode, md, sig );
642             if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] )
643               {
644                 if( is_selfsig )
645                   *is_selfsig = 1;
646                 rc = do_check( pk, sig, md, r_expired, NULL, ret_pk );
647               }
648             else if (check_pk)
649               rc=do_check(check_pk,sig,md,r_expired,NULL,ret_pk);
650             else
651               rc=signature_check2(sig,md,r_expiredate,r_expired,NULL,ret_pk);
652
653             cache_sig_result ( sig, rc );
654             gcry_md_close(md);
655         }
656         else
657           {
658             if (!opt.quiet)
659               log_info ("key %s: no user ID for key signature packet"
660                         " of class %02x\n",keystr_from_pk(pk),sig->sig_class);
661             rc = G10ERR_SIG_CLASS;
662           }
663     }
664
665     return rc;
666 }