1cdf63a70SMartin Matuska /*- 2cdf63a70SMartin Matuska * Copyright (c) 2014 Michihiro NAKAJIMA 3cdf63a70SMartin Matuska * All rights reserved. 4cdf63a70SMartin Matuska * 5cdf63a70SMartin Matuska * Redistribution and use in source and binary forms, with or without 6cdf63a70SMartin Matuska * modification, are permitted provided that the following conditions 7cdf63a70SMartin Matuska * are met: 8cdf63a70SMartin Matuska * 1. Redistributions of source code must retain the above copyright 9cdf63a70SMartin Matuska * notice, this list of conditions and the following disclaimer 10cdf63a70SMartin Matuska * in this position and unchanged. 11cdf63a70SMartin Matuska * 2. Redistributions in binary form must reproduce the above copyright 12cdf63a70SMartin Matuska * notice, this list of conditions and the following disclaimer in the 13cdf63a70SMartin Matuska * documentation and/or other materials provided with the distribution. 14cdf63a70SMartin Matuska * 15cdf63a70SMartin Matuska * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 16cdf63a70SMartin Matuska * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17cdf63a70SMartin Matuska * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18cdf63a70SMartin Matuska * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 19cdf63a70SMartin Matuska * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20cdf63a70SMartin Matuska * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21cdf63a70SMartin Matuska * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22cdf63a70SMartin Matuska * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23cdf63a70SMartin Matuska * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24cdf63a70SMartin Matuska * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25cdf63a70SMartin Matuska */ 26fae5c36eSMartin Matuska /* $OpenBSD: readpassphrase.c,v 1.27 2019/01/25 00:19:25 millert Exp $ */ 27fae5c36eSMartin Matuska 28cdf63a70SMartin Matuska /* 29fae5c36eSMartin Matuska * Copyright (c) 2000-2002, 2007, 2010 30fae5c36eSMartin Matuska * Todd C. Miller <millert@openbsd.org> 31cdf63a70SMartin Matuska * 32cdf63a70SMartin Matuska * Permission to use, copy, modify, and distribute this software for any 33cdf63a70SMartin Matuska * purpose with or without fee is hereby granted, provided that the above 34cdf63a70SMartin Matuska * copyright notice and this permission notice appear in all copies. 35cdf63a70SMartin Matuska * 36cdf63a70SMartin Matuska * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 37cdf63a70SMartin Matuska * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 38cdf63a70SMartin Matuska * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 39cdf63a70SMartin Matuska * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 40cdf63a70SMartin Matuska * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 41cdf63a70SMartin Matuska * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 42cdf63a70SMartin Matuska * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 43cdf63a70SMartin Matuska * 44cdf63a70SMartin Matuska * Sponsored in part by the Defense Advanced Research Projects 45cdf63a70SMartin Matuska * Agency (DARPA) and Air Force Research Laboratory, Air Force 46cdf63a70SMartin Matuska * Materiel Command, USAF, under agreement number F39502-99-1-0512. 47cdf63a70SMartin Matuska */ 48cdf63a70SMartin Matuska 49cdf63a70SMartin Matuska /* OPENBSD ORIGINAL: lib/libc/gen/readpassphrase.c */ 50cdf63a70SMartin Matuska 51cdf63a70SMartin Matuska 52cdf63a70SMartin Matuska #include "lafe_platform.h" 53cdf63a70SMartin Matuska #include <errno.h> 54cdf63a70SMartin Matuska #ifdef HAVE_STDLIB_H 55cdf63a70SMartin Matuska #include <stdlib.h> 56cdf63a70SMartin Matuska #endif 57cdf63a70SMartin Matuska #ifdef HAVE_UNISTD_H 58cdf63a70SMartin Matuska #include <unistd.h> 59cdf63a70SMartin Matuska #endif 60cdf63a70SMartin Matuska #ifdef HAVE_READPASSPHRASE_H 61cdf63a70SMartin Matuska #include <readpassphrase.h> 62cdf63a70SMartin Matuska #endif 63cdf63a70SMartin Matuska 64cdf63a70SMartin Matuska #include "err.h" 65cdf63a70SMartin Matuska #include "passphrase.h" 66cdf63a70SMartin Matuska 67cdf63a70SMartin Matuska #ifndef HAVE_READPASSPHRASE 68cdf63a70SMartin Matuska 69cdf63a70SMartin Matuska #define RPP_ECHO_OFF 0x00 /* Turn off echo (default). */ 70cdf63a70SMartin Matuska #define RPP_ECHO_ON 0x01 /* Leave echo on. */ 71cdf63a70SMartin Matuska #define RPP_REQUIRE_TTY 0x02 /* Fail if there is no tty. */ 72cdf63a70SMartin Matuska #define RPP_FORCELOWER 0x04 /* Force input to lower case. */ 73cdf63a70SMartin Matuska #define RPP_FORCEUPPER 0x08 /* Force input to upper case. */ 74cdf63a70SMartin Matuska #define RPP_SEVENBIT 0x10 /* Strip the high bit from input. */ 75cdf63a70SMartin Matuska #define RPP_STDIN 0x20 /* Read from stdin, not /dev/tty */ 76cdf63a70SMartin Matuska 77cdf63a70SMartin Matuska 78cdf63a70SMartin Matuska #if defined(_WIN32) && !defined(__CYGWIN__) 79*13d826ffSMartin Matuska #include <string.h> 80cdf63a70SMartin Matuska #include <windows.h> 81cdf63a70SMartin Matuska 82cdf63a70SMartin Matuska static char * 83cdf63a70SMartin Matuska readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) 84cdf63a70SMartin Matuska { 85cdf63a70SMartin Matuska HANDLE hStdin, hStdout; 86cdf63a70SMartin Matuska DWORD mode, rbytes; 87cdf63a70SMartin Matuska BOOL success; 88cdf63a70SMartin Matuska 89cdf63a70SMartin Matuska (void)flags; 90cdf63a70SMartin Matuska 91cdf63a70SMartin Matuska hStdin = GetStdHandle(STD_INPUT_HANDLE); 92cdf63a70SMartin Matuska if (hStdin == INVALID_HANDLE_VALUE) 93cdf63a70SMartin Matuska return (NULL); 94cdf63a70SMartin Matuska hStdout = GetStdHandle(STD_OUTPUT_HANDLE); 95cdf63a70SMartin Matuska if (hStdout == INVALID_HANDLE_VALUE) 96cdf63a70SMartin Matuska return (NULL); 97cdf63a70SMartin Matuska 98cdf63a70SMartin Matuska success = GetConsoleMode(hStdin, &mode); 99cdf63a70SMartin Matuska if (!success) 100cdf63a70SMartin Matuska return (NULL); 101cdf63a70SMartin Matuska mode &= ~ENABLE_ECHO_INPUT; 102cdf63a70SMartin Matuska mode |= ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; 103cdf63a70SMartin Matuska success = SetConsoleMode(hStdin, mode); 104cdf63a70SMartin Matuska if (!success) 105cdf63a70SMartin Matuska return (NULL); 106cdf63a70SMartin Matuska 107cdf63a70SMartin Matuska success = WriteFile(hStdout, prompt, (DWORD)strlen(prompt), 108cdf63a70SMartin Matuska NULL, NULL); 109cdf63a70SMartin Matuska if (!success) 110cdf63a70SMartin Matuska return (NULL); 111cdf63a70SMartin Matuska success = ReadFile(hStdin, buf, (DWORD)bufsiz - 1, &rbytes, NULL); 112cdf63a70SMartin Matuska if (!success) 113cdf63a70SMartin Matuska return (NULL); 114cdf63a70SMartin Matuska WriteFile(hStdout, "\r\n", 2, NULL, NULL); 115cdf63a70SMartin Matuska buf[rbytes] = '\0'; 116cdf63a70SMartin Matuska /* Remove trailing carriage return(s). */ 117*13d826ffSMartin Matuska buf[strcspn(buf, "\r\n")] = '\0'; 118cdf63a70SMartin Matuska 119cdf63a70SMartin Matuska return (buf); 120cdf63a70SMartin Matuska } 121cdf63a70SMartin Matuska 122cdf63a70SMartin Matuska #else /* _WIN32 && !__CYGWIN__ */ 123cdf63a70SMartin Matuska 124f061a221SMartin Matuska #include <assert.h> 125cdf63a70SMartin Matuska #include <ctype.h> 126cdf63a70SMartin Matuska #include <fcntl.h> 127cdf63a70SMartin Matuska #ifdef HAVE_PATHS_H 128cdf63a70SMartin Matuska #include <paths.h> 129cdf63a70SMartin Matuska #endif 130f061a221SMartin Matuska #include <signal.h> 131cdf63a70SMartin Matuska #include <string.h> 132f061a221SMartin Matuska #include <termios.h> 133cdf63a70SMartin Matuska #include <unistd.h> 134cdf63a70SMartin Matuska 135ae5876eaSMartin Matuska #ifndef _PATH_TTY 136ae5876eaSMartin Matuska #define _PATH_TTY "/dev/tty" 137ae5876eaSMartin Matuska #endif 138ae5876eaSMartin Matuska 139cdf63a70SMartin Matuska #ifdef TCSASOFT 140cdf63a70SMartin Matuska # define _T_FLUSH (TCSAFLUSH|TCSASOFT) 141cdf63a70SMartin Matuska #else 142cdf63a70SMartin Matuska # define _T_FLUSH (TCSAFLUSH) 143cdf63a70SMartin Matuska #endif 144cdf63a70SMartin Matuska 145cdf63a70SMartin Matuska /* SunOS 4.x which lacks _POSIX_VDISABLE, but has VDISABLE */ 146cdf63a70SMartin Matuska #if !defined(_POSIX_VDISABLE) && defined(VDISABLE) 147cdf63a70SMartin Matuska # define _POSIX_VDISABLE VDISABLE 148cdf63a70SMartin Matuska #endif 149cdf63a70SMartin Matuska 150f061a221SMartin Matuska #define M(a,b) (a > b ? a : b) 151f061a221SMartin Matuska #define MAX_SIGNO M(M(M(SIGALRM, SIGHUP), \ 152f061a221SMartin Matuska M(SIGINT, SIGPIPE)), \ 153f061a221SMartin Matuska M(M(SIGQUIT, SIGTERM), \ 154f061a221SMartin Matuska M(M(SIGTSTP, SIGTTIN), SIGTTOU))) 155f061a221SMartin Matuska 156f061a221SMartin Matuska static volatile sig_atomic_t signo[MAX_SIGNO + 1]; 157cdf63a70SMartin Matuska 158cdf63a70SMartin Matuska static void 159cdf63a70SMartin Matuska handler(int s) 160cdf63a70SMartin Matuska { 161f061a221SMartin Matuska assert(s <= MAX_SIGNO); 162cdf63a70SMartin Matuska signo[s] = 1; 163cdf63a70SMartin Matuska } 164cdf63a70SMartin Matuska 165cdf63a70SMartin Matuska static char * 166cdf63a70SMartin Matuska readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) 167cdf63a70SMartin Matuska { 168cdf63a70SMartin Matuska ssize_t nr; 169cdf63a70SMartin Matuska int input, output, save_errno, i, need_restart; 170cdf63a70SMartin Matuska char ch, *p, *end; 171cdf63a70SMartin Matuska struct termios term, oterm; 172e64fe029SMartin Matuska #ifdef HAVE_SIGACTION 173cdf63a70SMartin Matuska struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm; 174cdf63a70SMartin Matuska struct sigaction savetstp, savettin, savettou, savepipe; 175e64fe029SMartin Matuska #endif 176cdf63a70SMartin Matuska 177cdf63a70SMartin Matuska /* I suppose we could alloc on demand in this case (XXX). */ 178cdf63a70SMartin Matuska if (bufsiz == 0) { 179cdf63a70SMartin Matuska errno = EINVAL; 180cdf63a70SMartin Matuska return(NULL); 181cdf63a70SMartin Matuska } 182cdf63a70SMartin Matuska 183cdf63a70SMartin Matuska restart: 184f061a221SMartin Matuska for (i = 0; i <= MAX_SIGNO; i++) 185cdf63a70SMartin Matuska signo[i] = 0; 186cdf63a70SMartin Matuska nr = -1; 187cdf63a70SMartin Matuska save_errno = 0; 188cdf63a70SMartin Matuska need_restart = 0; 189cdf63a70SMartin Matuska /* 190cdf63a70SMartin Matuska * Read and write to /dev/tty if available. If not, read from 191cdf63a70SMartin Matuska * stdin and write to stderr unless a tty is required. 192cdf63a70SMartin Matuska */ 193cdf63a70SMartin Matuska if ((flags & RPP_STDIN) || 194cdf63a70SMartin Matuska (input = output = open(_PATH_TTY, O_RDWR)) == -1) { 195cdf63a70SMartin Matuska if (flags & RPP_REQUIRE_TTY) { 196cdf63a70SMartin Matuska errno = ENOTTY; 197cdf63a70SMartin Matuska return(NULL); 198cdf63a70SMartin Matuska } 199cdf63a70SMartin Matuska input = STDIN_FILENO; 200cdf63a70SMartin Matuska output = STDERR_FILENO; 201cdf63a70SMartin Matuska } 202cdf63a70SMartin Matuska 203cdf63a70SMartin Matuska /* 204fae5c36eSMartin Matuska * Turn off echo if possible. 205fae5c36eSMartin Matuska * If we are using a tty but are not the foreground pgrp this will 206fae5c36eSMartin Matuska * generate SIGTTOU, so do it *before* installing the signal handlers. 207fae5c36eSMartin Matuska */ 208fae5c36eSMartin Matuska if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) { 209fae5c36eSMartin Matuska memcpy(&term, &oterm, sizeof(term)); 210fae5c36eSMartin Matuska if (!(flags & RPP_ECHO_ON)) 211fae5c36eSMartin Matuska term.c_lflag &= ~(ECHO | ECHONL); 212fae5c36eSMartin Matuska #ifdef VSTATUS 213fae5c36eSMartin Matuska if (term.c_cc[VSTATUS] != _POSIX_VDISABLE) 214fae5c36eSMartin Matuska term.c_cc[VSTATUS] = _POSIX_VDISABLE; 215fae5c36eSMartin Matuska #endif 216fae5c36eSMartin Matuska (void)tcsetattr(input, _T_FLUSH, &term); 217fae5c36eSMartin Matuska } else { 218fae5c36eSMartin Matuska memset(&term, 0, sizeof(term)); 219fae5c36eSMartin Matuska term.c_lflag |= ECHO; 220fae5c36eSMartin Matuska memset(&oterm, 0, sizeof(oterm)); 221fae5c36eSMartin Matuska oterm.c_lflag |= ECHO; 222fae5c36eSMartin Matuska } 223fae5c36eSMartin Matuska 224e64fe029SMartin Matuska #ifdef HAVE_SIGACTION 225fae5c36eSMartin Matuska /* 226cdf63a70SMartin Matuska * Catch signals that would otherwise cause the user to end 227cdf63a70SMartin Matuska * up with echo turned off in the shell. Don't worry about 228cdf63a70SMartin Matuska * things like SIGXCPU and SIGVTALRM for now. 229cdf63a70SMartin Matuska */ 230cdf63a70SMartin Matuska sigemptyset(&sa.sa_mask); 231cdf63a70SMartin Matuska sa.sa_flags = 0; /* don't restart system calls */ 232cdf63a70SMartin Matuska sa.sa_handler = handler; 233f061a221SMartin Matuska /* Keep this list in sync with MAX_SIGNO! */ 234cdf63a70SMartin Matuska (void)sigaction(SIGALRM, &sa, &savealrm); 235cdf63a70SMartin Matuska (void)sigaction(SIGHUP, &sa, &savehup); 236cdf63a70SMartin Matuska (void)sigaction(SIGINT, &sa, &saveint); 237cdf63a70SMartin Matuska (void)sigaction(SIGPIPE, &sa, &savepipe); 238cdf63a70SMartin Matuska (void)sigaction(SIGQUIT, &sa, &savequit); 239cdf63a70SMartin Matuska (void)sigaction(SIGTERM, &sa, &saveterm); 240cdf63a70SMartin Matuska (void)sigaction(SIGTSTP, &sa, &savetstp); 241cdf63a70SMartin Matuska (void)sigaction(SIGTTIN, &sa, &savettin); 242cdf63a70SMartin Matuska (void)sigaction(SIGTTOU, &sa, &savettou); 243e64fe029SMartin Matuska #endif 244cdf63a70SMartin Matuska 245cdf63a70SMartin Matuska if (!(flags & RPP_STDIN)) { 246cdf63a70SMartin Matuska int r = write(output, prompt, strlen(prompt)); 247cdf63a70SMartin Matuska (void)r; 248cdf63a70SMartin Matuska } 249cdf63a70SMartin Matuska end = buf + bufsiz - 1; 250cdf63a70SMartin Matuska p = buf; 251cdf63a70SMartin Matuska while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') { 252cdf63a70SMartin Matuska if (p < end) { 253cdf63a70SMartin Matuska if ((flags & RPP_SEVENBIT)) 254cdf63a70SMartin Matuska ch &= 0x7f; 255f061a221SMartin Matuska if (isalpha((unsigned char)ch)) { 256cdf63a70SMartin Matuska if ((flags & RPP_FORCELOWER)) 257f061a221SMartin Matuska ch = (char)tolower((unsigned char)ch); 258cdf63a70SMartin Matuska if ((flags & RPP_FORCEUPPER)) 259f061a221SMartin Matuska ch = (char)toupper((unsigned char)ch); 260cdf63a70SMartin Matuska } 261cdf63a70SMartin Matuska *p++ = ch; 262cdf63a70SMartin Matuska } 263cdf63a70SMartin Matuska } 264cdf63a70SMartin Matuska *p = '\0'; 265cdf63a70SMartin Matuska save_errno = errno; 266cdf63a70SMartin Matuska if (!(term.c_lflag & ECHO)) { 267cdf63a70SMartin Matuska int r = write(output, "\n", 1); 268cdf63a70SMartin Matuska (void)r; 269cdf63a70SMartin Matuska } 270cdf63a70SMartin Matuska 271cdf63a70SMartin Matuska /* Restore old terminal settings and signals. */ 272cdf63a70SMartin Matuska if (memcmp(&term, &oterm, sizeof(term)) != 0) { 273fae5c36eSMartin Matuska const int sigttou = signo[SIGTTOU]; 274fae5c36eSMartin Matuska 275fae5c36eSMartin Matuska /* Ignore SIGTTOU generated when we are not the fg pgrp. */ 276cdf63a70SMartin Matuska while (tcsetattr(input, _T_FLUSH, &oterm) == -1 && 277fae5c36eSMartin Matuska errno == EINTR && !signo[SIGTTOU]) 278cdf63a70SMartin Matuska continue; 279fae5c36eSMartin Matuska signo[SIGTTOU] = sigttou; 280cdf63a70SMartin Matuska } 281e64fe029SMartin Matuska #ifdef HAVE_SIGACTION 282cdf63a70SMartin Matuska (void)sigaction(SIGALRM, &savealrm, NULL); 283cdf63a70SMartin Matuska (void)sigaction(SIGHUP, &savehup, NULL); 284cdf63a70SMartin Matuska (void)sigaction(SIGINT, &saveint, NULL); 285cdf63a70SMartin Matuska (void)sigaction(SIGQUIT, &savequit, NULL); 286cdf63a70SMartin Matuska (void)sigaction(SIGPIPE, &savepipe, NULL); 287cdf63a70SMartin Matuska (void)sigaction(SIGTERM, &saveterm, NULL); 288cdf63a70SMartin Matuska (void)sigaction(SIGTSTP, &savetstp, NULL); 289cdf63a70SMartin Matuska (void)sigaction(SIGTTIN, &savettin, NULL); 290cdf63a70SMartin Matuska (void)sigaction(SIGTTOU, &savettou, NULL); 291e64fe029SMartin Matuska #endif 292cdf63a70SMartin Matuska if (input != STDIN_FILENO) 293cdf63a70SMartin Matuska (void)close(input); 294cdf63a70SMartin Matuska 295cdf63a70SMartin Matuska /* 296cdf63a70SMartin Matuska * If we were interrupted by a signal, resend it to ourselves 297cdf63a70SMartin Matuska * now that we have restored the signal handlers. 298cdf63a70SMartin Matuska */ 299f061a221SMartin Matuska for (i = 0; i <= MAX_SIGNO; i++) { 300cdf63a70SMartin Matuska if (signo[i]) { 301cdf63a70SMartin Matuska kill(getpid(), i); 302cdf63a70SMartin Matuska switch (i) { 303cdf63a70SMartin Matuska case SIGTSTP: 304cdf63a70SMartin Matuska case SIGTTIN: 305cdf63a70SMartin Matuska case SIGTTOU: 306cdf63a70SMartin Matuska need_restart = 1; 307cdf63a70SMartin Matuska } 308cdf63a70SMartin Matuska } 309cdf63a70SMartin Matuska } 310cdf63a70SMartin Matuska if (need_restart) 311cdf63a70SMartin Matuska goto restart; 312cdf63a70SMartin Matuska 313cdf63a70SMartin Matuska if (save_errno) 314cdf63a70SMartin Matuska errno = save_errno; 315cdf63a70SMartin Matuska return(nr == -1 ? NULL : buf); 316cdf63a70SMartin Matuska } 317cdf63a70SMartin Matuska #endif /* _WIN32 && !__CYGWIN__ */ 318cdf63a70SMartin Matuska #endif /* HAVE_READPASSPHRASE */ 319cdf63a70SMartin Matuska 320cdf63a70SMartin Matuska char * 321cdf63a70SMartin Matuska lafe_readpassphrase(const char *prompt, char *buf, size_t bufsiz) 322cdf63a70SMartin Matuska { 323cdf63a70SMartin Matuska char *p; 324cdf63a70SMartin Matuska 325cdf63a70SMartin Matuska p = readpassphrase(prompt, buf, bufsiz, RPP_ECHO_OFF); 326cdf63a70SMartin Matuska if (p == NULL) { 327cdf63a70SMartin Matuska switch (errno) { 328cdf63a70SMartin Matuska case EINTR: 329cdf63a70SMartin Matuska break; 330cdf63a70SMartin Matuska default: 331cdf63a70SMartin Matuska lafe_errc(1, errno, "Couldn't read passphrase"); 332b9128a37SMartin Matuska /* NOTREACHED */ 333cdf63a70SMartin Matuska } 334cdf63a70SMartin Matuska } 335cdf63a70SMartin Matuska return (p); 336cdf63a70SMartin Matuska } 337cdf63a70SMartin Matuska 338