/* pipemode.c - pipemode handler * Copyright (C) 1998, 1990, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include #include #include "options.h" #include "packet.h" #include "errors.h" #include "iobuf.h" #include "keydb.h" #include "memory.h" #include "util.h" #include "main.h" #include "status.h" #include "filter.h" #define CONTROL_PACKET_SPACE 30 #define FAKED_LITERAL_PACKET_SPACE (9+2+2) enum pipemode_state_e { STX_init = 0, STX_wait_operation, STX_begin, STX_text, STX_detached_signature, STX_detached_signature_wait_text, STX_signed_data, STX_wait_init }; struct pipemode_context_s { enum pipemode_state_e state; int operation; int stop; int block_mode; UnarmorPump unarmor_ctx; }; static size_t make_control ( byte *buf, int code, int operation ) { const byte *sesmark; size_t sesmarklen, n=0;; sesmark = get_session_marker( &sesmarklen ); if ( sesmarklen > 20 ) BUG(); buf[n++] = 0xff; /* new format, type 63, 1 length byte */ n++; /* length will fixed below */ memcpy(buf+n, sesmark, sesmarklen ); n+= sesmarklen; buf[n++] = CTRLPKT_PIPEMODE; buf[n++] = code; buf[n++] = operation; buf[1] = n-2; return n; } static int pipemode_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len) { size_t size = *ret_len; struct pipemode_context_s *stx = opaque; int rc=0; size_t n = 0; int esc = 0; if( control == IOBUFCTRL_UNDERFLOW ) { *ret_len = 0; /* reserve some space for one control packet */ if ( size <= CONTROL_PACKET_SPACE+FAKED_LITERAL_PACKET_SPACE ) BUG(); size -= CONTROL_PACKET_SPACE+FAKED_LITERAL_PACKET_SPACE; if ( stx->block_mode ) { /* reserve 2 bytes for the block length */ buf[n++] = 0; buf[n++] = 0; } while ( n < size ) { /* FIXME: we have to make sure that we have a large enough * buffer for a control packet even after we already read * something. The easest way to do this is probably by ungetting * the control sequence and returning the buffer we have * already assembled */ int c = iobuf_get (a); if (c == -1) { if ( stx->state != STX_init ) { log_error ("EOF encountered at wrong state\n"); stx->stop = 1; return -1; } break; } if ( esc ) { switch (c) { case '@': if ( stx->state == STX_text ) { buf[n++] = c; break; } else if ( stx->state == STX_detached_signature ) { esc = 0; goto do_unarmor; /* not a very elegant solution */ } else if ( stx->state == STX_detached_signature_wait_text) { esc = 0; break; /* just ignore it in this state */ } log_error ("@@ not allowed in current state\n"); return -1; case '<': /* begin of stream part */ if ( stx->state != STX_init ) { log_error ("nested begin of stream\n"); stx->stop = 1; return -1; } stx->state = STX_wait_operation; stx->block_mode = 0; unarmor_pump_release (stx->unarmor_ctx); stx->unarmor_ctx = NULL; break; case '>': /* end of stream part */ if ( stx->state != STX_wait_init ) { log_error ("invalid state for @>\n"); stx->stop = 1; return -1; } stx->state = STX_init; break; case 'V': /* operation = verify */ case 'E': /* operation = encrypt */ case 'S': /* operation = sign */ case 'B': /* operation = detach sign */ case 'C': /* operation = clearsign */ case 'D': /* operation = decrypt */ if ( stx->state != STX_wait_operation ) { log_error ("invalid state for operation code\n"); stx->stop = 1; return -1; } stx->operation = c; if ( stx->operation == 'B') { stx->state = STX_detached_signature; if ( !opt.no_armor ) stx->unarmor_ctx = unarmor_pump_new (); } else stx->state = STX_begin; n += make_control ( buf+n, 1, stx->operation ); /* must leave after a control packet */ goto leave; case 't': /* plaintext text follows */ if ( stx->state == STX_detached_signature_wait_text ) stx->state = STX_detached_signature; if ( stx->state == STX_detached_signature ) { if ( stx->operation != 'B' ) { log_error ("invalid operation for this state\n"); stx->stop = 1; return -1; } stx->state = STX_signed_data; n += make_control ( buf+n, 2, 'B' ); /* and now we fake a literal data packet much the same * as in armor.c */ buf[n++] = 0xaf; /* old packet format, type 11, var length */ buf[n++] = 0; /* set the length header */ buf[n++] = 6; buf[n++] = 'b'; /* we ignore it anyway */ buf[n++] = 0; /* namelength */ memset(buf+n, 0, 4); /* timestamp */ n += 4; /* and return now so that we are sure to have * more space in the bufer for the next control * packet */ stx->block_mode = 1; goto leave2; } else { log_error ("invalid state for @t\n"); stx->stop = 1; return -1; } break; case '.': /* ready */ if ( stx->state == STX_signed_data ) { if (stx->block_mode) { buf[0] = (n-2) >> 8; buf[1] = (n-2); if ( buf[0] || buf[1] ) { /* end of blocks marker */ buf[n++] = 0; buf[n++] = 0; } stx->block_mode = 0; } n += make_control ( buf+n, 3, 'B' ); } else { log_error ("invalid state for @.\n"); stx->stop = 1; return -1; } stx->state = STX_wait_init; goto leave; default: log_error ("invalid escape sequence 0x%02x in stream\n", c); stx->stop = 1; return -1; } esc = 0; } else if (c == '@') esc = 1; else if (stx->unarmor_ctx) { do_unarmor: /* used to handle a @@ */ c = unarmor_pump (stx->unarmor_ctx, c); if ( !(c & ~255) ) buf[n++] = c; else if ( c < 0 ) { /* end of armor or error - we don't care becuase the armor can be modified anyway. The unarmored stuff should stand for itself. */ unarmor_pump_release (stx->unarmor_ctx); stx->unarmor_ctx = NULL; stx->state = STX_detached_signature_wait_text; } } else if (stx->state == STX_detached_signature_wait_text) ; /* just wait */ else buf[n++] = c; } leave: if ( !n ) { stx->stop = 1; rc = -1; /* eof */ } if ( stx->block_mode ) { /* fixup the block length */ buf[0] = (n-2) >> 8; buf[1] = (n-2); } leave2: /*log_hexdump ("pipemode:", buf, n );*/ *ret_len = n; } else if( control == IOBUFCTRL_DESC ) *(char**)buf = "pipemode_filter"; return rc; } void run_in_pipemode(void) { IOBUF fp; armor_filter_context_t afx; struct pipemode_context_s stx; int rc; memset( &afx, 0, sizeof afx); memset( &stx, 0, sizeof stx); fp = iobuf_open("-"); iobuf_push_filter (fp, pipemode_filter, &stx ); do { write_status (STATUS_BEGIN_STREAM); rc = proc_packets( NULL, fp ); write_status (STATUS_END_STREAM); } while ( !stx.stop ); }