Fix ARCFOUR.
[shishi.git] / lib / kdc.c
blob3ca678453cd4e6e6a0588fd732718cc307f41e99
1 /* kdc.c Key distribution (AS/TGS) functions
2 * Copyright (C) 2002, 2003 Simon Josefsson
4 * This file is part of Shishi.
6 * Shishi is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * Shishi is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Shishi; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "internal.h"
24 /**
25 * shishi_as_derive_salt:
26 * @handle: shishi handle as allocated by shishi_init().
27 * @asreq: input AS-REQ variable.
28 * @asrep: input AS-REP variable.
29 * @salt: output array with salt.
30 * @saltlen: on input, maximum size of output array with salt, on output,
31 * holds actual size of output array with salt.
33 * Derive the salt that should be used when deriving a key via
34 * shishi_string_to_key() for an AS exchange. Currently this searches
35 * for PA-DATA of type SHISHI_PA_PW_SALT in the AS-REP and returns it
36 * if found, otherwise the salt is derived from the client name and
37 * realm in AS-REQ.
39 * Return value: Returns SHISHI_OK iff successful.
40 **/
41 int
42 shishi_as_derive_salt (Shishi * handle,
43 Shishi_asn1 asreq,
44 Shishi_asn1 asrep, char *salt, size_t * saltlen)
46 size_t len = *saltlen;
47 size_t tmplen;
48 char *format;
49 int res;
50 int i, n;
52 res = shishi_asn1_number_of_elements (handle, asrep, "padata", &n);
53 if (res == SHISHI_ASN1_NO_ELEMENT)
54 n = 0;
55 else if (res != SHISHI_OK)
56 return res;
58 for (i = 1; i <= n; i++)
60 int patype;
62 asprintf (&format, "padata.?%d.padata-type", i);
63 res = shishi_asn1_read_int32 (handle, asrep, format, &patype);
64 free (format);
65 if (res != SHISHI_OK)
66 return res;
68 if (patype == SHISHI_PA_PW_SALT)
70 asprintf (&format, "padata.?%d.padata-value", i);
71 res = shishi_asn1_read (handle, asrep, format, salt, saltlen);
72 free (format);
73 if (res != SHISHI_OK)
74 return res;
76 return SHISHI_OK;
80 len = *saltlen;
81 res = shishi_asn1_read (handle, asreq, "req-body.realm", salt, &len);
82 if (res != SHISHI_OK)
83 return res;
85 res = shishi_asn1_number_of_elements (handle, asreq,
86 "req-body.cname.name-string", &n);
87 if (res != SHISHI_OK)
88 return res;
90 for (i = 1; i <= n; i++)
92 if (*saltlen < len)
93 return SHISHI_TOO_SMALL_BUFFER;
94 tmplen = *saltlen - len;
96 asprintf (&format, "req-body.cname.name-string.?%d", i);
97 res = shishi_asn1_read (handle, asreq, format, salt + len, &tmplen);
98 free (format);
99 if (res != SHISHI_OK)
100 return res;
102 len += tmplen;
105 *saltlen = len;
107 return SHISHI_OK;
111 shishi_kdcreq_sendrecv (Shishi * handle, Shishi_asn1 kdcreq,
112 Shishi_asn1 * kdcrep)
114 char *der;
115 size_t der_len;
116 size_t buflen;
117 char *buffer;
118 char *realm;
119 size_t realmlen;
120 int res;
122 res = shishi_new_a2d (handle, kdcreq, &der, &der_len);
123 if (res != SHISHI_OK)
125 shishi_error_printf (handle, "Could not DER encode AS-REQ: %s\n",
126 shishi_strerror (res));
127 return res;
130 res = shishi_asn1_read2 (handle, kdcreq, "req-body.realm",
131 &realm, &realmlen);
132 if (res != SHISHI_OK)
134 shishi_error_printf (handle, "Could not get realm: %s\n",
135 shishi_error (handle));
136 return res;
138 realm = xrealloc (realm, realmlen + 1);
139 realm[realmlen] = '\0';
141 res = shishi_kdc_sendrecv (handle, realm, der, der_len, &buffer, &buflen);
142 if (res != SHISHI_OK)
144 shishi_error_printf (handle, "Could not send to KDC: %s\n",
145 shishi_error (handle));
146 return res;
148 free (realm);
149 free (der);
151 if (VERBOSEASN1 (handle))
152 printf ("received %d bytes\n", buflen);
154 *kdcrep = shishi_der2asn1_asrep (handle, buffer, buflen);
155 if (*kdcrep == NULL)
157 *kdcrep = shishi_der2asn1_tgsrep (handle, buffer, buflen);
158 if (*kdcrep == NULL)
160 *kdcrep = shishi_der2asn1_kdcrep (handle, buffer, buflen);
161 if (*kdcrep == NULL)
163 *kdcrep = shishi_der2asn1_krberror (handle, buffer, buflen);
164 if (*kdcrep == NULL)
166 shishi_error_printf
167 (handle, "Could not DER decode AS-REP/KRB-ERROR: %s",
168 shishi_error (handle));
169 return SHISHI_ASN1_ERROR;
172 shishi_error_clear (handle);
173 return SHISHI_GOT_KRBERROR;
175 else
177 printf
178 ("Buggy server replied with KDC-REP instead of AS-REP\n");
182 free (buffer);
184 return SHISHI_OK;
188 * shishi_kdc_copy_crealm:
189 * @handle: shishi handle as allocated by shishi_init().
190 * @kdcrep: KDC-REP to read crealm from.
191 * @encticketpart: EncTicketPart to set crealm in.
193 * Set crealm in KDC-REP to value in EncTicketPart.
195 * Return value: Returns SHISHI_OK if successful.
198 shishi_kdc_copy_crealm (Shishi * handle,
199 Shishi_asn1 kdcrep, Shishi_asn1 encticketpart)
201 char *buf;
202 size_t buflen;
203 int res;
205 res = shishi_asn1_read2 (handle, encticketpart, "crealm", &buf, &buflen);
206 if (res != SHISHI_OK)
207 return res;
209 res = shishi_asn1_write (handle, kdcrep, "crealm", buf, buflen);
210 free (buf);
211 if (res != SHISHI_OK)
212 return res;
214 return SHISHI_OK;
218 * shishi_as_check_crealm:
219 * @handle: shishi handle as allocated by shishi_init().
220 * @asreq: AS-REQ to compare realm field in.
221 * @asrep: AS-REP to compare realm field in.
223 * Verify that AS-REQ.req-body.realm and AS-REP.crealm fields matches.
224 * This is one of the steps that has to be performed when processing a
225 * AS-REQ and AS-REP exchange, see shishi_kdc_process().
227 * Return value: Returns SHISHI_OK if successful,
228 * SHISHI_REALM_MISMATCH if the values differ, or an error code.
231 shishi_as_check_crealm (Shishi * handle, Shishi_asn1 asreq, Shishi_asn1 asrep)
233 char *reqrealm, *reprealm;
234 size_t reqrealmlen, reprealmlen;
235 int res;
237 res = shishi_asn1_read2 (handle, asreq, "req-body.realm",
238 &reqrealm, &reqrealmlen);
239 if (res != SHISHI_OK)
241 shishi_error_printf (handle, "Could not read request realm: %s\n",
242 shishi_strerror (res));
243 return res;
246 res = shishi_asn1_read2 (handle, asrep, "crealm", &reprealm, &reprealmlen);
247 if (res != SHISHI_OK)
249 shishi_error_printf (handle, "Could not read reply realm: %s\n",
250 shishi_strerror (res));
251 return res;
254 reqrealm[reqrealmlen] = '\0';
255 reprealm[reprealmlen] = '\0';
257 if (VERBOSEASN1 (handle))
259 printf ("request realm: %s\n", reqrealm);
260 printf ("reply realm: %s\n", reprealm);
263 res = strcmp (reqrealm, reprealm) != 0;
265 free (reqrealm);
266 free (reprealm);
268 if (res)
269 return SHISHI_REALM_MISMATCH;
271 return SHISHI_OK;
275 * shishi_kdc_copy_cname:
276 * @handle: shishi handle as allocated by shishi_init().
277 * @kdcrep: KDC-REQ to read cname from.
278 * @encticketpart: EncTicketPart to set cname in.
280 * Set cname in KDC-REP to value in EncTicketPart.
282 * Return value: Returns SHISHI_OK if successful.
285 shishi_kdc_copy_cname (Shishi * handle,
286 Shishi_asn1 kdcrep, Shishi_asn1 encticketpart)
288 char *buf;
289 char *format;
290 size_t buflen;
291 int res;
292 int i, n;
294 res = shishi_asn1_read2 (handle, encticketpart,
295 "cname.name-type", &buf, &buflen);
296 if (res != SHISHI_OK)
297 return res;
299 res = shishi_asn1_write (handle, kdcrep, "cname.name-type", buf, buflen);
300 free (buf);
301 if (res != SHISHI_OK)
302 return res;
304 res = shishi_asn1_number_of_elements (handle, encticketpart,
305 "cname.name-string", &n);
306 if (res != SHISHI_OK)
307 return res;
309 res = shishi_asn1_write (handle, kdcrep, "cname.name-string", NULL, 0);
310 if (res != SHISHI_OK)
311 return res;
313 for (i = 1; i <= n; i++)
315 res = shishi_asn1_write (handle, kdcrep, "cname.name-string", "NEW", 1);
316 if (res != SHISHI_OK)
317 return res;
319 asprintf (&format, "cname.name-string.?%d", i);
320 res = shishi_asn1_read2 (handle, encticketpart, format, &buf, &buflen);
321 free (format);
322 if (res != SHISHI_OK)
323 return res;
325 asprintf (&format, "cname.name-string.?%d", i);
326 res = shishi_asn1_write (handle, kdcrep, format, buf, buflen);
327 free (format);
328 free (buf);
329 if (res != SHISHI_OK)
330 return res;
333 return SHISHI_OK;
337 * shishi_as_check_cname:
338 * @handle: shishi handle as allocated by shishi_init().
339 * @asreq: AS-REQ to compare client name field in.
340 * @asrep: AS-REP to compare client name field in.
342 * Verify that AS-REQ.req-body.realm and AS-REP.crealm fields matches.
343 * This is one of the steps that has to be performed when processing a
344 * AS-REQ and AS-REP exchange, see shishi_kdc_process().
346 * Return value: Returns SHISHI_OK if successful,
347 * SHISHI_CNAME_MISMATCH if the values differ, or an error code.
350 shishi_as_check_cname (Shishi * handle, Shishi_asn1 asreq, Shishi_asn1 asrep)
352 char *reqcname, *repcname;
353 size_t reqcnamelen, repcnamelen;
354 char *format;
355 int res;
356 int i, j;
358 /* We do not compare msg-type as recommended on the ietf-krb-wg list */
360 res = shishi_asn1_number_of_elements (handle, asreq,
361 "req-body.cname.name-string", &i);
362 if (res != SHISHI_OK)
363 return res;
365 res = shishi_asn1_number_of_elements (handle, asrep,
366 "cname.name-string", &j);
367 if (res != SHISHI_OK)
368 return res;
370 if (i != j)
371 return SHISHI_CNAME_MISMATCH;
373 for (i = 1; i <= j; i++)
375 asprintf (&format, "req-body.cname.name-string.?%d", i);
376 res =
377 shishi_asn1_read2 (handle, asreq, format, &reqcname, &reqcnamelen);
378 free (format);
379 if (res != SHISHI_OK)
380 return res;
382 asprintf (&format, "cname.name-string.?%d", i);
383 res =
384 shishi_asn1_read2 (handle, asrep, format, &repcname, &repcnamelen);
385 free (format);
386 if (res != SHISHI_OK)
387 return res;
389 if (VERBOSEASN1 (handle))
391 reqcname[reqcnamelen] = '\0';
392 repcname[repcnamelen] = '\0';
393 printf ("request cname %d: %s\n", i, reqcname);
394 printf ("reply cname %d: %s\n", i, repcname);
397 res = (reqcnamelen != repcnamelen) ||
398 (memcmp (reqcname, repcname, reqcnamelen) != 0);
400 free (reqcname);
401 free (repcname);
403 if (res)
404 return SHISHI_CNAME_MISMATCH;
407 return SHISHI_OK;
411 * shishi_kdc_copy_nonce:
412 * @handle: shishi handle as allocated by shishi_init().
413 * @kdcreq: KDC-REQ to read nonce from.
414 * @enckdcreppart: EncKDCRepPart to set nonce in.
416 * Set nonce in EncKDCRepPart to value in KDC-REQ.
418 * Return value: Returns SHISHI_OK if successful.
421 shishi_kdc_copy_nonce (Shishi * handle,
422 Shishi_asn1 kdcreq, Shishi_asn1 enckdcreppart)
424 int res;
425 uint32_t nonce;
427 res = shishi_kdcreq_nonce (handle, kdcreq, &nonce);
428 if (res != SHISHI_OK)
429 return res;
431 res = shishi_enckdcreppart_nonce_set (handle, enckdcreppart, nonce);
432 if (res != SHISHI_OK)
433 return res;
435 return SHISHI_OK;
438 static int
439 shishi_kdc_check_nonce_1 (Shishi * handle,
440 char *reqnonce, size_t reqnoncelen,
441 char *repnonce, size_t repnoncelen)
443 if (VERBOSENOICE (handle))
445 size_t i;
447 printf ("request nonce (len=%d) ", reqnoncelen);
448 for (i = 0; i < reqnoncelen; i++)
449 printf ("%02x", reqnonce[i] & 0xFF);
450 printf ("\n");
451 printf ("reply nonce (len=%d) ", repnoncelen);
452 for (i = 0; i < repnoncelen; i++)
453 printf ("%02x", repnonce[i] & 0xFF);
454 printf ("\n");
457 if (reqnoncelen > 4 && repnoncelen == 4)
459 /* This case warrants some explanation.
461 * RFC 1510 didn't restrict nonce to 4 bytes, so the nonce field
462 * may be longer. There are KDCs that will accept longer nonces
463 * but truncated them to 4 bytes in the response. If we happen
464 * to parse such a KDC request, we consider it OK even though it
465 * isn't. I doubt this is a security problem, because you need
466 * to break the integrity protection of the encryption system
467 * as well as guess the nonce correctly. The nonce doesn't seem
468 * to serve any purpose at all, really.
472 if (memcmp (reqnonce + reqnoncelen - 4, repnonce, 4) != 0)
473 return SHISHI_NONCE_MISMATCH;
475 shishi_warn (handle, "server truncated long nonce to 4 bytes");
477 return SHISHI_OK;
480 if (reqnoncelen != repnoncelen ||
481 memcmp (reqnonce, repnonce, repnoncelen) != 0)
482 return SHISHI_NONCE_MISMATCH;
484 return SHISHI_OK;
488 * shishi_kdc_check_nonce:
489 * @handle: shishi handle as allocated by shishi_init().
490 * @kdcreq: KDC-REQ to compare nonce field in.
491 * @enckdcreppart: Encrypted KDC-REP part to compare nonce field in.
493 * Verify that KDC-REQ.req-body.nonce and EncKDCRepPart.nonce fields
494 * matches. This is one of the steps that has to be performed when
495 * processing a KDC-REQ and KDC-REP exchange.
497 * Return value: Returns SHISHI_OK if successful,
498 * SHISHI_NONCE_LENGTH_MISMATCH if the nonces have different lengths
499 * (usually indicates that buggy server truncated nonce to 4 bytes),
500 * SHISHI_NONCE_MISMATCH if the values differ, or an error code.
503 shishi_kdc_check_nonce (Shishi * handle,
504 Shishi_asn1 kdcreq, Shishi_asn1 enckdcreppart)
506 char *reqnonce;
507 char *repnonce;
508 size_t reqnoncelen, repnoncelen;
509 int res;
511 res = shishi_asn1_read2 (handle, kdcreq, "req-body.nonce",
512 &reqnonce, &reqnoncelen);
513 if (res != SHISHI_OK)
515 shishi_error_printf (handle, "Could not read request nonce: %s\n",
516 shishi_strerror (res));
517 return res;
520 res = shishi_asn1_read2 (handle, enckdcreppart, "nonce",
521 &repnonce, &repnoncelen);
522 if (res != SHISHI_OK)
524 free (reqnonce);
525 shishi_error_printf (handle, "Could not read reply nonce: %s\n",
526 shishi_strerror (res));
527 return res;
530 res = shishi_kdc_check_nonce_1 (handle, reqnonce, reqnoncelen,
531 repnonce, repnoncelen);
533 free (reqnonce);
534 free (repnonce);
536 return res;
540 * shishi_tgs_process:
541 * @handle: shishi handle as allocated by shishi_init().
542 * @tgsreq: input variable that holds the sent KDC-REQ.
543 * @tgsrep: input variable that holds the received KDC-REP.
544 * @authenticator: input variable with Authenticator from AP-REQ in KDC-REQ.
545 * @oldenckdcreppart: input variable with EncKDCRepPart used in request.
546 * @enckdcreppart: output variable that holds new EncKDCRepPart.
548 * Process a TGS client exchange and output decrypted EncKDCRepPart
549 * which holds details for the new ticket received. This function
550 * simply derives the encryption key from the ticket used to construct
551 * the TGS request and calls shishi_kdc_process(), which see.
553 * Return value: Returns SHISHI_OK iff the TGS client exchange was
554 * successful.
557 shishi_tgs_process (Shishi * handle,
558 Shishi_asn1 tgsreq,
559 Shishi_asn1 tgsrep,
560 Shishi_asn1 authenticator,
561 Shishi_asn1 oldenckdcreppart, Shishi_asn1 * enckdcreppart)
563 Shishi_key *tktkey;
564 Shishi_key *subkey;
565 int use_subkey;
566 int etype;
567 int res;
569 res = shishi_kdcrep_get_enc_part_etype (handle, tgsrep, &etype);
570 if (res != SHISHI_OK)
571 return res;
573 res = shishi_authenticator_get_subkey (handle, authenticator, &subkey);
574 use_subkey = (res != SHISHI_ASN1_NO_ELEMENT);
575 if (res != SHISHI_OK && res != SHISHI_ASN1_NO_ELEMENT)
576 return res;
578 res = shishi_enckdcreppart_get_key (handle, oldenckdcreppart, &tktkey);
579 if (res != SHISHI_OK)
580 return res;
582 if (etype != shishi_key_type (use_subkey ? subkey : tktkey))
583 res = SHISHI_TGSREP_BAD_KEYTYPE;
584 else
585 res = shishi_kdc_process (handle, tgsreq, tgsrep,
586 use_subkey ? subkey : tktkey,
587 use_subkey ?
588 SHISHI_KEYUSAGE_ENCTGSREPPART_AUTHENTICATOR_KEY
589 : SHISHI_KEYUSAGE_ENCTGSREPPART_SESSION_KEY,
590 enckdcreppart);
592 /* Entire if statement to work around buggy KDCs. */
593 if (use_subkey && (res == SHISHI_CRYPTO_ERROR ||
594 res == SHISHI_TGSREP_BAD_KEYTYPE))
596 int tmpres;
598 /* Try again using key from ticket instead of subkey */
599 if (etype != shishi_key_type (tktkey))
600 tmpres = SHISHI_TGSREP_BAD_KEYTYPE;
601 else
602 tmpres = shishi_kdc_process (handle, tgsreq, tgsrep, tktkey,
603 SHISHI_KEYUSAGE_ENCTGSREPPART_SESSION_KEY,
604 enckdcreppart);
606 /* if bug workaround code didn't help, return original error. */
607 if (tmpres != SHISHI_OK)
608 return res;
610 shishi_warn (handle, "KDC bug: Reply encrypted using wrong key.");
612 res = tmpres;
615 if (res != SHISHI_OK)
616 return res;
618 return SHISHI_OK;
622 * shishi_as_process:
623 * @handle: shishi handle as allocated by shishi_init().
624 * @asreq: input variable that holds the sent KDC-REQ.
625 * @asrep: input variable that holds the received KDC-REP.
626 * @string: input variable with zero terminated password.
627 * @enckdcreppart: output variable that holds new EncKDCRepPart.
629 * Process an AS client exchange and output decrypted EncKDCRepPart
630 * which holds details for the new ticket received. This function
631 * simply derives the encryption key from the password and calls
632 * shishi_kdc_process(), which see.
634 * Return value: Returns SHISHI_OK iff the AS client exchange was
635 * successful.
638 shishi_as_process (Shishi * handle,
639 Shishi_asn1 asreq,
640 Shishi_asn1 asrep,
641 const char *string, Shishi_asn1 * enckdcreppart)
643 unsigned char salt[BUFSIZ];
644 size_t saltlen;
645 int res;
646 Shishi_key *key;
647 int keytype;
649 saltlen = sizeof (salt);
650 res = shishi_as_derive_salt (handle, asreq, asrep, salt, &saltlen);
651 if (res != SHISHI_OK)
652 return res;
654 res = shishi_kdcrep_get_enc_part_etype (handle, asrep, &keytype);
655 if (res != SHISHI_OK)
656 return res;
658 res = shishi_key_from_string (handle, keytype,
659 string, strlen (string),
660 salt, saltlen, NULL, &key);
661 if (res != SHISHI_OK)
662 return res;
664 if (VERBOSENOICE (handle))
665 shishi_key_print (handle, stderr, key);
667 res = shishi_kdc_process (handle, asreq, asrep, key,
668 SHISHI_KEYUSAGE_ENCASREPPART, enckdcreppart);
670 return res;
674 * shishi_kdc_process:
675 * @handle: shishi handle as allocated by shishi_init().
676 * @kdcreq: input variable that holds the sent KDC-REQ.
677 * @kdcrep: input variable that holds the received KDC-REP.
678 * @key: input array with key to decrypt encrypted part of KDC-REP with.
679 * @keyusage: kereros key usage value.
680 * @enckdcreppart: output variable that holds new EncKDCRepPart.
682 * Process a KDC client exchange and output decrypted EncKDCRepPart
683 * which holds details for the new ticket received. Use
684 * shishi_kdcrep_get_ticket() to extract the ticket. This function
685 * verifies the various conditions that must hold if the response is
686 * to be considered valid, specifically it compares nonces
687 * (shishi_check_nonces()) and if the exchange was a AS exchange, it
688 * also compares cname and crealm (shishi_check_cname() and
689 * shishi_check_crealm()).
691 * Usually the shishi_as_process() and shishi_tgs_process() functions
692 * should be used instead, since they simplify the decryption key
693 * computation.
695 * Return value: Returns SHISHI_OK iff the KDC client exchange was
696 * successful.
699 shishi_kdc_process (Shishi * handle,
700 Shishi_asn1 kdcreq,
701 Shishi_asn1 kdcrep,
702 Shishi_key * key, int keyusage,
703 Shishi_asn1 * enckdcreppart)
705 int res;
706 int msgtype;
709 If the reply message type is KRB_AS_REP, then the client verifies
710 that the cname and crealm fields in the cleartext portion of the
711 reply match what it requested. If any padata fields are present,
712 they may be used to derive the proper secret key to decrypt the
713 message. The client decrypts the encrypted part of the response
714 using its secret key, verifies that the nonce in the encrypted
715 part matches the nonce it supplied in its request (to detect
716 replays). It also verifies that the sname and srealm in the
717 response match those in the request (or are otherwise expected
718 values), and that the host address field is also correct. It then
719 stores the ticket, session key, start and expiration times, and
720 other information for later use. The key-expiration field from the
721 encrypted part of the response may be checked to notify the user
722 of impending key expiration (the client program could then suggest
723 remedial action, such as a password change).
726 msgtype = 0;
727 res = shishi_asn1_read_integer (handle, kdcrep, "msg-type", &msgtype);
728 if (res != SHISHI_OK)
729 return res;
731 if (msgtype == SHISHI_MSGTYPE_AS_REP)
733 res = shishi_as_check_crealm (handle, kdcreq, kdcrep);
734 if (res != SHISHI_OK)
735 return res;
737 res = shishi_as_check_cname (handle, kdcreq, kdcrep);
738 if (res != SHISHI_OK)
739 return res;
742 res = shishi_kdcrep_decrypt (handle, kdcrep, key, keyusage, enckdcreppart);
743 if (res != SHISHI_OK)
744 return res;
746 res = shishi_kdc_check_nonce (handle, kdcreq, *enckdcreppart);
747 if (res != SHISHI_OK)
748 return res;
750 return SHISHI_OK;