1 /* call-agent.c - Divert GPGSM operations to the agent
2 * Copyright (C) 2001, 2002, 2003, 2005, 2007,
3 * 2008, 2009 Free Software Foundation, Inc.
5 * This file is part of GnuPG.
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.
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.
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/>.
38 #include "keydb.h" /* fixme: Move this to import.c */
42 static assuan_context_t agent_ctx
= NULL
;
49 const unsigned char *ciphertext
;
57 const unsigned char *sexp
;
71 /* Try to connect to the agent via socket or fork it off and work by
72 pipes. Handle the server's initial greeting */
74 start_agent (ctrl_t ctrl
)
79 rc
= 0; /* fixme: We need a context for each thread or
80 serialize the access to the agent (which is
81 suitable given that the agent is not MT. */
84 rc
= start_new_gpg_agent (&agent_ctx
,
85 GPG_ERR_SOURCE_DEFAULT
,
88 opt
.display
, opt
.ttyname
, opt
.ttytype
,
89 opt
.lc_ctype
, opt
.lc_messages
,
90 opt
.xauthority
, opt
.pinentry_user_data
,
91 opt
.verbose
, DBG_ASSUAN
,
96 /* Tell the agent that we support Pinentry notifications. No
97 error checking so that it will work also with older
99 assuan_transact (agent_ctx
, "OPTION allow-pinentry-notify",
100 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
104 if (!ctrl
->agent_seen
)
106 ctrl
->agent_seen
= 1;
107 audit_log_ok (ctrl
->audit
, AUDIT_AGENT_READY
, rc
);
116 membuf_data_cb (void *opaque
, const void *buffer
, size_t length
)
118 membuf_t
*data
= opaque
;
121 put_membuf (data
, buffer
, length
);
126 /* This is the default inquiry callback. It mainly handles the
127 Pinentry notifications. */
129 default_inq_cb (void *opaque
, const char *line
)
132 ctrl_t ctrl
= opaque
;
134 if (!strncmp (line
, "PINENTRY_LAUNCHED", 17) && (line
[17]==' '||!line
[17]))
136 err
= gpgsm_proxy_pinentry_notify (ctrl
, line
);
138 log_error (_("failed to proxy %s inquiry to client\n"),
139 "PINENTRY_LAUNCHED");
140 /* We do not pass errors to avoid breaking other code. */
143 log_error ("ignoring gpg-agent inquiry `%s'\n", line
);
151 /* Call the agent to do a sign operation using the key identified by
152 the hex string KEYGRIP. */
154 gpgsm_agent_pksign (ctrl_t ctrl
, const char *keygrip
, const char *desc
,
155 unsigned char *digest
, size_t digestlen
, int digestalgo
,
156 unsigned char **r_buf
, size_t *r_buflen
)
159 char *p
, line
[ASSUAN_LINELENGTH
];
164 rc
= start_agent (ctrl
);
168 if (digestlen
*2 + 50 > DIM(line
))
169 return gpg_error (GPG_ERR_GENERAL
);
171 rc
= assuan_transact (agent_ctx
, "RESET", NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
175 snprintf (line
, DIM(line
)-1, "SIGKEY %s", keygrip
);
176 line
[DIM(line
)-1] = 0;
177 rc
= assuan_transact (agent_ctx
, line
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
183 snprintf (line
, DIM(line
)-1, "SETKEYDESC %s", desc
);
184 line
[DIM(line
)-1] = 0;
185 rc
= assuan_transact (agent_ctx
, line
,
186 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
191 sprintf (line
, "SETHASH %d ", digestalgo
);
192 p
= line
+ strlen (line
);
193 for (i
=0; i
< digestlen
; i
++, p
+= 2 )
194 sprintf (p
, "%02X", digest
[i
]);
195 rc
= assuan_transact (agent_ctx
, line
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
199 init_membuf (&data
, 1024);
200 rc
= assuan_transact (agent_ctx
, "PKSIGN",
201 membuf_data_cb
, &data
, default_inq_cb
, ctrl
,
205 xfree (get_membuf (&data
, &len
));
208 *r_buf
= get_membuf (&data
, r_buflen
);
210 if (!gcry_sexp_canon_len (*r_buf
, *r_buflen
, NULL
, NULL
))
212 xfree (*r_buf
); *r_buf
= NULL
;
213 return gpg_error (GPG_ERR_INV_VALUE
);
216 return *r_buf
? 0 : out_of_core ();
220 /* Call the scdaemon to do a sign operation using the key identified by
221 the hex string KEYID. */
223 gpgsm_scd_pksign (ctrl_t ctrl
, const char *keyid
, const char *desc
,
224 unsigned char *digest
, size_t digestlen
, int digestalgo
,
225 unsigned char **r_buf
, size_t *r_buflen
)
228 char *p
, line
[ASSUAN_LINELENGTH
];
232 unsigned char *sigbuf
;
241 case GCRY_MD_SHA1
: hashopt
= "--hash=sha1"; break;
242 case GCRY_MD_RMD160
:hashopt
= "--hash=rmd160"; break;
243 case GCRY_MD_MD5
: hashopt
= "--hash=md5"; break;
244 case GCRY_MD_SHA256
:hashopt
= "--hash=sha256"; break;
246 return gpg_error (GPG_ERR_DIGEST_ALGO
);
249 rc
= start_agent (ctrl
);
253 if (digestlen
*2 + 50 > DIM(line
))
254 return gpg_error (GPG_ERR_GENERAL
);
256 p
= stpcpy (line
, "SCD SETDATA " );
257 for (i
=0; i
< digestlen
; i
++, p
+= 2 )
258 sprintf (p
, "%02X", digest
[i
]);
259 rc
= assuan_transact (agent_ctx
, line
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
263 init_membuf (&data
, 1024);
265 snprintf (line
, DIM(line
)-1, "SCD PKSIGN %s %s", hashopt
, keyid
);
266 line
[DIM(line
)-1] = 0;
267 rc
= assuan_transact (agent_ctx
, line
,
268 membuf_data_cb
, &data
, default_inq_cb
, ctrl
,
272 xfree (get_membuf (&data
, &len
));
275 sigbuf
= get_membuf (&data
, &sigbuflen
);
277 /* Create an S-expression from it which is formatted like this:
278 "(7:sig-val(3:rsa(1:sSIGBUFLEN:SIGBUF)))" Fixme: If a card ever
279 creates non-RSA keys we need to change things. */
280 *r_buflen
= 21 + 11 + sigbuflen
+ 4;
281 p
= xtrymalloc (*r_buflen
);
282 *r_buf
= (unsigned char*)p
;
288 p
= stpcpy (p
, "(7:sig-val(3:rsa(1:s" );
289 sprintf (p
, "%u:", (unsigned int)sigbuflen
);
291 memcpy (p
, sigbuf
, sigbuflen
);
296 assert (gcry_sexp_canon_len (*r_buf
, *r_buflen
, NULL
, NULL
));
303 /* Handle a CIPHERTEXT inquiry. Note, we only send the data,
304 assuan_transact talkes care of flushing and writing the end */
306 inq_ciphertext_cb (void *opaque
, const char *line
)
308 struct cipher_parm_s
*parm
= opaque
;
311 if (!strncmp (line
, "CIPHERTEXT", 10) && (line
[10]==' '||!line
[10]))
313 assuan_begin_confidential (parm
->ctx
);
314 rc
= assuan_send_data (parm
->ctx
, parm
->ciphertext
, parm
->ciphertextlen
);
315 assuan_end_confidential (parm
->ctx
);
318 rc
= default_inq_cb (parm
->ctrl
, line
);
324 /* Call the agent to do a decrypt operation using the key identified by
325 the hex string KEYGRIP. */
327 gpgsm_agent_pkdecrypt (ctrl_t ctrl
, const char *keygrip
, const char *desc
,
328 ksba_const_sexp_t ciphertext
,
329 char **r_buf
, size_t *r_buflen
)
332 char line
[ASSUAN_LINELENGTH
];
334 struct cipher_parm_s cipher_parm
;
336 char *p
, *buf
, *endp
;
337 size_t ciphertextlen
;
339 if (!keygrip
|| strlen(keygrip
) != 40 || !ciphertext
|| !r_buf
|| !r_buflen
)
340 return gpg_error (GPG_ERR_INV_VALUE
);
343 ciphertextlen
= gcry_sexp_canon_len (ciphertext
, 0, NULL
, NULL
);
345 return gpg_error (GPG_ERR_INV_VALUE
);
347 rc
= start_agent (ctrl
);
351 rc
= assuan_transact (agent_ctx
, "RESET", NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
355 assert ( DIM(line
) >= 50 );
356 snprintf (line
, DIM(line
)-1, "SETKEY %s", keygrip
);
357 line
[DIM(line
)-1] = 0;
358 rc
= assuan_transact (agent_ctx
, line
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
364 snprintf (line
, DIM(line
)-1, "SETKEYDESC %s", desc
);
365 line
[DIM(line
)-1] = 0;
366 rc
= assuan_transact (agent_ctx
, line
,
367 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
372 init_membuf (&data
, 1024);
373 cipher_parm
.ctrl
= ctrl
;
374 cipher_parm
.ctx
= agent_ctx
;
375 cipher_parm
.ciphertext
= ciphertext
;
376 cipher_parm
.ciphertextlen
= ciphertextlen
;
377 rc
= assuan_transact (agent_ctx
, "PKDECRYPT",
378 membuf_data_cb
, &data
,
379 inq_ciphertext_cb
, &cipher_parm
, NULL
, NULL
);
382 xfree (get_membuf (&data
, &len
));
386 put_membuf (&data
, "", 1); /* Make sure it is 0 terminated. */
387 buf
= get_membuf (&data
, &len
);
389 return gpg_error (GPG_ERR_ENOMEM
);
390 assert (len
); /* (we forced Nul termination.) */
394 if (len
< 13 || memcmp (buf
, "(5:value", 8) ) /* "(5:valueN:D)\0" */
395 return gpg_error (GPG_ERR_INV_SEXP
);
396 len
-= 11; /* Count only the data of the second part. */
397 p
= buf
+ 8; /* Skip leading parenthesis and the value tag. */
401 /* For compatibility with older gpg-agents handle the old style
402 incomplete S-exps. */
403 len
--; /* Do not count the Nul. */
407 n
= strtoul (p
, &endp
, 10);
408 if (!n
|| *endp
!= ':')
409 return gpg_error (GPG_ERR_INV_SEXP
);
412 return gpg_error (GPG_ERR_INV_SEXP
); /* Oops: Inconsistent S-Exp. */
414 memmove (buf
, endp
, n
);
425 /* Handle a KEYPARMS inquiry. Note, we only send the data,
426 assuan_transact takes care of flushing and writing the end */
428 inq_genkey_parms (void *opaque
, const char *line
)
430 struct genkey_parm_s
*parm
= opaque
;
433 if (!strncmp (line
, "KEYPARAM", 8) && (line
[8]==' '||!line
[8]))
435 rc
= assuan_send_data (parm
->ctx
, parm
->sexp
, parm
->sexplen
);
438 rc
= default_inq_cb (parm
->ctrl
, line
);
445 /* Call the agent to generate a newkey */
447 gpgsm_agent_genkey (ctrl_t ctrl
,
448 ksba_const_sexp_t keyparms
, ksba_sexp_t
*r_pubkey
)
451 struct genkey_parm_s gk_parm
;
457 rc
= start_agent (ctrl
);
461 rc
= assuan_transact (agent_ctx
, "RESET", NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
465 init_membuf (&data
, 1024);
467 gk_parm
.ctx
= agent_ctx
;
468 gk_parm
.sexp
= keyparms
;
469 gk_parm
.sexplen
= gcry_sexp_canon_len (keyparms
, 0, NULL
, NULL
);
470 if (!gk_parm
.sexplen
)
471 return gpg_error (GPG_ERR_INV_VALUE
);
472 rc
= assuan_transact (agent_ctx
, "GENKEY",
473 membuf_data_cb
, &data
,
474 inq_genkey_parms
, &gk_parm
, NULL
, NULL
);
477 xfree (get_membuf (&data
, &len
));
480 buf
= get_membuf (&data
, &len
);
482 return gpg_error (GPG_ERR_ENOMEM
);
483 if (!gcry_sexp_canon_len (buf
, len
, NULL
, NULL
))
486 return gpg_error (GPG_ERR_INV_SEXP
);
493 /* Call the agent to read the public key part for a given keygrip. If
494 FROMCARD is true, the key is directly read from the current
495 smartcard. In this case HEXKEYGRIP should be the keyID
498 gpgsm_agent_readkey (ctrl_t ctrl
, int fromcard
, const char *hexkeygrip
,
499 ksba_sexp_t
*r_pubkey
)
505 char line
[ASSUAN_LINELENGTH
];
508 rc
= start_agent (ctrl
);
512 rc
= assuan_transact (agent_ctx
, "RESET",NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
516 snprintf (line
, DIM(line
)-1, "%sREADKEY %s",
517 fromcard
? "SCD ":"", hexkeygrip
);
518 line
[DIM(line
)-1] = 0;
520 init_membuf (&data
, 1024);
521 rc
= assuan_transact (agent_ctx
, line
,
522 membuf_data_cb
, &data
,
523 default_inq_cb
, ctrl
, NULL
, NULL
);
526 xfree (get_membuf (&data
, &len
));
529 buf
= get_membuf (&data
, &len
);
531 return gpg_error (GPG_ERR_ENOMEM
);
532 if (!gcry_sexp_canon_len (buf
, len
, NULL
, NULL
))
535 return gpg_error (GPG_ERR_INV_SEXP
);
544 istrusted_status_cb (void *opaque
, const char *line
)
546 struct rootca_flags_s
*flags
= opaque
;
548 if (!strncmp (line
, "TRUSTLISTFLAG", 13) && (line
[13]==' ' || !line
[13]))
550 for (line
+= 13; *line
== ' '; line
++)
552 if (!strncmp (line
, "relax", 5) && (line
[5] == ' ' || !line
[5]))
554 else if (!strncmp (line
, "cm", 2) && (line
[2] == ' ' || !line
[2]))
555 flags
->chain_model
= 1;
562 /* Ask the agent whether the certificate is in the list of trusted
563 keys. The certificate is either specified by the CERT object or by
564 the fingerprint HEXFPR. ROOTCA_FLAGS is guaranteed to be cleared
567 gpgsm_agent_istrusted (ctrl_t ctrl
, ksba_cert_t cert
, const char *hexfpr
,
568 struct rootca_flags_s
*rootca_flags
)
571 char line
[ASSUAN_LINELENGTH
];
573 memset (rootca_flags
, 0, sizeof *rootca_flags
);
576 return gpg_error (GPG_ERR_INV_ARG
);
578 rc
= start_agent (ctrl
);
584 snprintf (line
, DIM(line
)-1, "ISTRUSTED %s", hexfpr
);
585 line
[DIM(line
)-1] = 0;
591 fpr
= gpgsm_get_fingerprint_hexstring (cert
, GCRY_MD_SHA1
);
594 log_error ("error getting the fingerprint\n");
595 return gpg_error (GPG_ERR_GENERAL
);
598 snprintf (line
, DIM(line
)-1, "ISTRUSTED %s", fpr
);
599 line
[DIM(line
)-1] = 0;
603 rc
= assuan_transact (agent_ctx
, line
, NULL
, NULL
, NULL
, NULL
,
604 istrusted_status_cb
, rootca_flags
);
606 rootca_flags
->valid
= 1;
610 /* Ask the agent to mark CERT as a trusted Root-CA one */
612 gpgsm_agent_marktrusted (ctrl_t ctrl
, ksba_cert_t cert
)
615 char *fpr
, *dn
, *dnfmt
;
616 char line
[ASSUAN_LINELENGTH
];
618 rc
= start_agent (ctrl
);
622 fpr
= gpgsm_get_fingerprint_hexstring (cert
, GCRY_MD_SHA1
);
625 log_error ("error getting the fingerprint\n");
626 return gpg_error (GPG_ERR_GENERAL
);
629 dn
= ksba_cert_get_issuer (cert
, 0);
633 return gpg_error (GPG_ERR_GENERAL
);
635 dnfmt
= gpgsm_format_name2 (dn
, 0);
638 return gpg_error_from_syserror ();
639 snprintf (line
, DIM(line
)-1, "MARKTRUSTED %s S %s", fpr
, dnfmt
);
640 line
[DIM(line
)-1] = 0;
644 rc
= assuan_transact (agent_ctx
, line
, NULL
, NULL
,
645 default_inq_cb
, ctrl
, NULL
, NULL
);
651 /* Ask the agent whether the a corresponding secret key is available
652 for the given keygrip */
654 gpgsm_agent_havekey (ctrl_t ctrl
, const char *hexkeygrip
)
657 char line
[ASSUAN_LINELENGTH
];
659 rc
= start_agent (ctrl
);
663 if (!hexkeygrip
|| strlen (hexkeygrip
) != 40)
664 return gpg_error (GPG_ERR_INV_VALUE
);
666 snprintf (line
, DIM(line
)-1, "HAVEKEY %s", hexkeygrip
);
667 line
[DIM(line
)-1] = 0;
669 rc
= assuan_transact (agent_ctx
, line
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
675 learn_status_cb (void *opaque
, const char *line
)
677 struct learn_parm_s
*parm
= opaque
;
679 /* Pass progress data to the caller. */
680 if (!strncmp (line
, "PROGRESS", 8) && (line
[8]==' ' || !line
[8]))
684 for (line
+= 8; *line
== ' '; line
++)
686 if (gpgsm_status (parm
->ctrl
, STATUS_PROGRESS
, line
))
687 return gpg_error (GPG_ERR_ASS_CANCELED
);
694 learn_cb (void *opaque
, const void *buffer
, size_t length
)
696 struct learn_parm_s
*parm
= opaque
;
707 put_membuf (parm
->data
, buffer
, length
);
710 /* END encountered - process what we have */
711 buf
= get_membuf (parm
->data
, &len
);
714 parm
->error
= gpg_error (GPG_ERR_ENOMEM
);
718 if (gpgsm_status (parm
->ctrl
, STATUS_PROGRESS
, "learncard C 0 0"))
719 return gpg_error (GPG_ERR_ASS_CANCELED
);
721 /* FIXME: this should go into import.c */
722 rc
= ksba_cert_new (&cert
);
728 rc
= ksba_cert_init_from_mem (cert
, buf
, len
);
731 log_error ("failed to parse a certificate: %s\n", gpg_strerror (rc
));
732 ksba_cert_release (cert
);
737 rc
= gpgsm_basic_cert_check (parm
->ctrl
, cert
);
738 if (gpg_err_code (rc
) == GPG_ERR_MISSING_CERT
)
739 { /* For later use we store it in the ephemeral database. */
740 log_info ("issuer certificate missing - storing as ephemeral\n");
741 keydb_store_cert (cert
, 1, NULL
);
744 log_error ("invalid certificate: %s\n", gpg_strerror (rc
));
749 if (!keydb_store_cert (cert
, 0, &existed
))
751 if (opt
.verbose
> 1 && existed
)
752 log_info ("certificate already in DB\n");
753 else if (opt
.verbose
&& !existed
)
754 log_info ("certificate imported\n");
758 ksba_cert_release (cert
);
759 init_membuf (parm
->data
, 4096);
763 /* Call the agent to learn about a smartcard */
765 gpgsm_agent_learn (ctrl_t ctrl
)
768 struct learn_parm_s learn_parm
;
772 rc
= start_agent (ctrl
);
776 init_membuf (&data
, 4096);
777 learn_parm
.error
= 0;
778 learn_parm
.ctrl
= ctrl
;
779 learn_parm
.ctx
= agent_ctx
;
780 learn_parm
.data
= &data
;
781 rc
= assuan_transact (agent_ctx
, "LEARN --send",
782 learn_cb
, &learn_parm
,
784 learn_status_cb
, &learn_parm
);
785 xfree (get_membuf (&data
, &len
));
788 return learn_parm
.error
;
792 /* Ask the agent to change the passphrase of the key identified by
793 HEXKEYGRIP. If DESC is not NULL, display instead of the default
794 description message. */
796 gpgsm_agent_passwd (ctrl_t ctrl
, const char *hexkeygrip
, const char *desc
)
799 char line
[ASSUAN_LINELENGTH
];
801 rc
= start_agent (ctrl
);
805 if (!hexkeygrip
|| strlen (hexkeygrip
) != 40)
806 return gpg_error (GPG_ERR_INV_VALUE
);
810 snprintf (line
, DIM(line
)-1, "SETKEYDESC %s", desc
);
811 line
[DIM(line
)-1] = 0;
812 rc
= assuan_transact (agent_ctx
, line
,
813 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
818 snprintf (line
, DIM(line
)-1, "PASSWD %s", hexkeygrip
);
819 line
[DIM(line
)-1] = 0;
821 rc
= assuan_transact (agent_ctx
, line
, NULL
, NULL
,
822 default_inq_cb
, ctrl
, NULL
, NULL
);
828 /* Ask the agent to pop up a confirmation dialog with the text DESC
829 and an okay and cancel button. */
831 gpgsm_agent_get_confirmation (ctrl_t ctrl
, const char *desc
)
834 char line
[ASSUAN_LINELENGTH
];
836 rc
= start_agent (ctrl
);
840 snprintf (line
, DIM(line
)-1, "GET_CONFIRMATION %s", desc
);
841 line
[DIM(line
)-1] = 0;
843 rc
= assuan_transact (agent_ctx
, line
, NULL
, NULL
,
844 default_inq_cb
, ctrl
, NULL
, NULL
);
850 /* Return 0 if the agent is alive. This is useful to make sure that
851 an agent has been started. */
853 gpgsm_agent_send_nop (ctrl_t ctrl
)
857 rc
= start_agent (ctrl
);
859 rc
= assuan_transact (agent_ctx
, "NOP",
860 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
867 keyinfo_status_cb (void *opaque
, const char *line
)
869 char **serialno
= opaque
;
872 if (!strncmp (line
, "KEYINFO ", 8) && !*serialno
)
874 s
= strchr (line
+8, ' ');
875 if (s
&& s
[1] == 'T' && s
[2] == ' ' && s
[3])
878 s2
= strchr (s
, ' ');
881 *serialno
= xtrymalloc ((s2
- s
)+1);
884 memcpy (*serialno
, s
, s2
- s
);
885 (*serialno
)[s2
- s
] = 0;
893 /* Return the serial number for a secret key. If the returned serial
894 number is NULL, the key is not stored on a smartcard. Caller needs
895 to free R_SERIALNO. */
897 gpgsm_agent_keyinfo (ctrl_t ctrl
, const char *hexkeygrip
, char **r_serialno
)
900 char line
[ASSUAN_LINELENGTH
];
901 char *serialno
= NULL
;
905 err
= start_agent (ctrl
);
909 if (!hexkeygrip
|| strlen (hexkeygrip
) != 40)
910 return gpg_error (GPG_ERR_INV_VALUE
);
912 snprintf (line
, DIM(line
)-1, "KEYINFO %s", hexkeygrip
);
913 line
[DIM(line
)-1] = 0;
915 err
= assuan_transact (agent_ctx
, line
, NULL
, NULL
, NULL
, NULL
,
916 keyinfo_status_cb
, &serialno
);
917 if (!err
&& serialno
)
919 /* Sanity check for bad characters. */
920 if (strpbrk (serialno
, ":\n\r"))
921 err
= GPG_ERR_INV_VALUE
;
926 *r_serialno
= serialno
;