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 __FBSDID("$FreeBSD$"); 54cdf63a70SMartin Matuska 55cdf63a70SMartin Matuska #include <errno.h> 56cdf63a70SMartin Matuska #ifdef HAVE_STDLIB_H 57cdf63a70SMartin Matuska #include <stdlib.h> 58cdf63a70SMartin Matuska #endif 59cdf63a70SMartin Matuska #ifdef HAVE_UNISTD_H 60cdf63a70SMartin Matuska #include <unistd.h> 61cdf63a70SMartin Matuska #endif 62cdf63a70SMartin Matuska #ifdef HAVE_READPASSPHRASE_H 63cdf63a70SMartin Matuska #include <readpassphrase.h> 64cdf63a70SMartin Matuska #endif 65cdf63a70SMartin Matuska 66cdf63a70SMartin Matuska #include "err.h" 67cdf63a70SMartin Matuska #include "passphrase.h" 68cdf63a70SMartin Matuska 69cdf63a70SMartin Matuska #ifndef HAVE_READPASSPHRASE 70cdf63a70SMartin Matuska 71cdf63a70SMartin Matuska #define RPP_ECHO_OFF 0x00 /* Turn off echo (default). */ 72cdf63a70SMartin Matuska #define RPP_ECHO_ON 0x01 /* Leave echo on. */ 73cdf63a70SMartin Matuska #define RPP_REQUIRE_TTY 0x02 /* Fail if there is no tty. */ 74cdf63a70SMartin Matuska #define RPP_FORCELOWER 0x04 /* Force input to lower case. */ 75cdf63a70SMartin Matuska #define RPP_FORCEUPPER 0x08 /* Force input to upper case. */ 76cdf63a70SMartin Matuska #define RPP_SEVENBIT 0x10 /* Strip the high bit from input. */ 77cdf63a70SMartin Matuska #define RPP_STDIN 0x20 /* Read from stdin, not /dev/tty */ 78cdf63a70SMartin Matuska 79cdf63a70SMartin Matuska 80cdf63a70SMartin Matuska #if defined(_WIN32) && !defined(__CYGWIN__) 81cdf63a70SMartin Matuska #include <windows.h> 82cdf63a70SMartin Matuska 83cdf63a70SMartin Matuska static char * 84cdf63a70SMartin Matuska readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) 85cdf63a70SMartin Matuska { 86cdf63a70SMartin Matuska HANDLE hStdin, hStdout; 87cdf63a70SMartin Matuska DWORD mode, rbytes; 88cdf63a70SMartin Matuska BOOL success; 89cdf63a70SMartin Matuska 90cdf63a70SMartin Matuska (void)flags; 91cdf63a70SMartin Matuska 92cdf63a70SMartin Matuska hStdin = GetStdHandle(STD_INPUT_HANDLE); 93cdf63a70SMartin Matuska if (hStdin == INVALID_HANDLE_VALUE) 94cdf63a70SMartin Matuska return (NULL); 95cdf63a70SMartin Matuska hStdout = GetStdHandle(STD_OUTPUT_HANDLE); 96cdf63a70SMartin Matuska if (hStdout == INVALID_HANDLE_VALUE) 97cdf63a70SMartin Matuska return (NULL); 98cdf63a70SMartin Matuska 99cdf63a70SMartin Matuska success = GetConsoleMode(hStdin, &mode); 100cdf63a70SMartin Matuska if (!success) 101cdf63a70SMartin Matuska return (NULL); 102cdf63a70SMartin Matuska mode &= ~ENABLE_ECHO_INPUT; 103cdf63a70SMartin Matuska mode |= ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; 104cdf63a70SMartin Matuska success = SetConsoleMode(hStdin, mode); 105cdf63a70SMartin Matuska if (!success) 106cdf63a70SMartin Matuska return (NULL); 107cdf63a70SMartin Matuska 108cdf63a70SMartin Matuska success = WriteFile(hStdout, prompt, (DWORD)strlen(prompt), 109cdf63a70SMartin Matuska NULL, NULL); 110cdf63a70SMartin Matuska if (!success) 111cdf63a70SMartin Matuska return (NULL); 112cdf63a70SMartin Matuska success = ReadFile(hStdin, buf, (DWORD)bufsiz - 1, &rbytes, NULL); 113cdf63a70SMartin Matuska if (!success) 114cdf63a70SMartin Matuska return (NULL); 115cdf63a70SMartin Matuska WriteFile(hStdout, "\r\n", 2, NULL, NULL); 116cdf63a70SMartin Matuska buf[rbytes] = '\0'; 117cdf63a70SMartin Matuska /* Remove trailing carriage return(s). */ 118cdf63a70SMartin Matuska if (rbytes > 2 && buf[rbytes - 2] == '\r' && buf[rbytes - 1] == '\n') 119cdf63a70SMartin Matuska buf[rbytes - 2] = '\0'; 120cdf63a70SMartin Matuska 121cdf63a70SMartin Matuska return (buf); 122cdf63a70SMartin Matuska } 123cdf63a70SMartin Matuska 124cdf63a70SMartin Matuska #else /* _WIN32 && !__CYGWIN__ */ 125cdf63a70SMartin Matuska 126f061a221SMartin Matuska #include <assert.h> 127cdf63a70SMartin Matuska #include <ctype.h> 128cdf63a70SMartin Matuska #include <fcntl.h> 129cdf63a70SMartin Matuska #ifdef HAVE_PATHS_H 130cdf63a70SMartin Matuska #include <paths.h> 131cdf63a70SMartin Matuska #endif 132f061a221SMartin Matuska #include <signal.h> 133cdf63a70SMartin Matuska #include <string.h> 134f061a221SMartin Matuska #include <termios.h> 135cdf63a70SMartin Matuska #include <unistd.h> 136cdf63a70SMartin Matuska 137ae5876eaSMartin Matuska #ifndef _PATH_TTY 138ae5876eaSMartin Matuska #define _PATH_TTY "/dev/tty" 139ae5876eaSMartin Matuska #endif 140ae5876eaSMartin Matuska 141cdf63a70SMartin Matuska #ifdef TCSASOFT 142cdf63a70SMartin Matuska # define _T_FLUSH (TCSAFLUSH|TCSASOFT) 143cdf63a70SMartin Matuska #else 144cdf63a70SMartin Matuska # define _T_FLUSH (TCSAFLUSH) 145cdf63a70SMartin Matuska #endif 146cdf63a70SMartin Matuska 147cdf63a70SMartin Matuska /* SunOS 4.x which lacks _POSIX_VDISABLE, but has VDISABLE */ 148cdf63a70SMartin Matuska #if !defined(_POSIX_VDISABLE) && defined(VDISABLE) 149cdf63a70SMartin Matuska # define _POSIX_VDISABLE VDISABLE 150cdf63a70SMartin Matuska #endif 151cdf63a70SMartin Matuska 152f061a221SMartin Matuska #define M(a,b) (a > b ? a : b) 153f061a221SMartin Matuska #define MAX_SIGNO M(M(M(SIGALRM, SIGHUP), \ 154f061a221SMartin Matuska M(SIGINT, SIGPIPE)), \ 155f061a221SMartin Matuska M(M(SIGQUIT, SIGTERM), \ 156f061a221SMartin Matuska M(M(SIGTSTP, SIGTTIN), SIGTTOU))) 157f061a221SMartin Matuska 158f061a221SMartin Matuska static volatile sig_atomic_t signo[MAX_SIGNO + 1]; 159cdf63a70SMartin Matuska 160cdf63a70SMartin Matuska static void 161cdf63a70SMartin Matuska handler(int s) 162cdf63a70SMartin Matuska { 163f061a221SMartin Matuska assert(s <= MAX_SIGNO); 164cdf63a70SMartin Matuska signo[s] = 1; 165cdf63a70SMartin Matuska } 166cdf63a70SMartin Matuska 167cdf63a70SMartin Matuska static char * 168cdf63a70SMartin Matuska readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) 169cdf63a70SMartin Matuska { 170cdf63a70SMartin Matuska ssize_t nr; 171cdf63a70SMartin Matuska int input, output, save_errno, i, need_restart; 172cdf63a70SMartin Matuska char ch, *p, *end; 173cdf63a70SMartin Matuska struct termios term, oterm; 174*e64fe029SMartin Matuska #ifdef HAVE_SIGACTION 175cdf63a70SMartin Matuska struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm; 176cdf63a70SMartin Matuska struct sigaction savetstp, savettin, savettou, savepipe; 177*e64fe029SMartin Matuska #endif 178cdf63a70SMartin Matuska 179cdf63a70SMartin Matuska /* I suppose we could alloc on demand in this case (XXX). */ 180cdf63a70SMartin Matuska if (bufsiz == 0) { 181cdf63a70SMartin Matuska errno = EINVAL; 182cdf63a70SMartin Matuska return(NULL); 183cdf63a70SMartin Matuska } 184cdf63a70SMartin Matuska 185cdf63a70SMartin Matuska restart: 186f061a221SMartin Matuska for (i = 0; i <= MAX_SIGNO; i++) 187cdf63a70SMartin Matuska signo[i] = 0; 188cdf63a70SMartin Matuska nr = -1; 189cdf63a70SMartin Matuska save_errno = 0; 190cdf63a70SMartin Matuska need_restart = 0; 191cdf63a70SMartin Matuska /* 192cdf63a70SMartin Matuska * Read and write to /dev/tty if available. If not, read from 193cdf63a70SMartin Matuska * stdin and write to stderr unless a tty is required. 194cdf63a70SMartin Matuska */ 195cdf63a70SMartin Matuska if ((flags & RPP_STDIN) || 196cdf63a70SMartin Matuska (input = output = open(_PATH_TTY, O_RDWR)) == -1) { 197cdf63a70SMartin Matuska if (flags & RPP_REQUIRE_TTY) { 198cdf63a70SMartin Matuska errno = ENOTTY; 199cdf63a70SMartin Matuska return(NULL); 200cdf63a70SMartin Matuska } 201cdf63a70SMartin Matuska input = STDIN_FILENO; 202cdf63a70SMartin Matuska output = STDERR_FILENO; 203cdf63a70SMartin Matuska } 204cdf63a70SMartin Matuska 205cdf63a70SMartin Matuska /* 206fae5c36eSMartin Matuska * Turn off echo if possible. 207fae5c36eSMartin Matuska * If we are using a tty but are not the foreground pgrp this will 208fae5c36eSMartin Matuska * generate SIGTTOU, so do it *before* installing the signal handlers. 209fae5c36eSMartin Matuska */ 210fae5c36eSMartin Matuska if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) { 211fae5c36eSMartin Matuska memcpy(&term, &oterm, sizeof(term)); 212fae5c36eSMartin Matuska if (!(flags & RPP_ECHO_ON)) 213fae5c36eSMartin Matuska term.c_lflag &= ~(ECHO | ECHONL); 214fae5c36eSMartin Matuska #ifdef VSTATUS 215fae5c36eSMartin Matuska if (term.c_cc[VSTATUS] != _POSIX_VDISABLE) 216fae5c36eSMartin Matuska term.c_cc[VSTATUS] = _POSIX_VDISABLE; 217fae5c36eSMartin Matuska #endif 218fae5c36eSMartin Matuska (void)tcsetattr(input, _T_FLUSH, &term); 219fae5c36eSMartin Matuska } else { 220fae5c36eSMartin Matuska memset(&term, 0, sizeof(term)); 221fae5c36eSMartin Matuska term.c_lflag |= ECHO; 222fae5c36eSMartin Matuska memset(&oterm, 0, sizeof(oterm)); 223fae5c36eSMartin Matuska oterm.c_lflag |= ECHO; 224fae5c36eSMartin Matuska } 225fae5c36eSMartin Matuska 226*e64fe029SMartin Matuska #ifdef HAVE_SIGACTION 227fae5c36eSMartin Matuska /* 228cdf63a70SMartin Matuska * Catch signals that would otherwise cause the user to end 229cdf63a70SMartin Matuska * up with echo turned off in the shell. Don't worry about 230cdf63a70SMartin Matuska * things like SIGXCPU and SIGVTALRM for now. 231cdf63a70SMartin Matuska */ 232cdf63a70SMartin Matuska sigemptyset(&sa.sa_mask); 233cdf63a70SMartin Matuska sa.sa_flags = 0; /* don't restart system calls */ 234cdf63a70SMartin Matuska sa.sa_handler = handler; 235f061a221SMartin Matuska /* Keep this list in sync with MAX_SIGNO! */ 236cdf63a70SMartin Matuska (void)sigaction(SIGALRM, &sa, &savealrm); 237cdf63a70SMartin Matuska (void)sigaction(SIGHUP, &sa, &savehup); 238cdf63a70SMartin Matuska (void)sigaction(SIGINT, &sa, &saveint); 239cdf63a70SMartin Matuska (void)sigaction(SIGPIPE, &sa, &savepipe); 240cdf63a70SMartin Matuska (void)sigaction(SIGQUIT, &sa, &savequit); 241cdf63a70SMartin Matuska (void)sigaction(SIGTERM, &sa, &saveterm); 242cdf63a70SMartin Matuska (void)sigaction(SIGTSTP, &sa, &savetstp); 243cdf63a70SMartin Matuska (void)sigaction(SIGTTIN, &sa, &savettin); 244cdf63a70SMartin Matuska (void)sigaction(SIGTTOU, &sa, &savettou); 245*e64fe029SMartin Matuska #endif 246cdf63a70SMartin Matuska 247cdf63a70SMartin Matuska if (!(flags & RPP_STDIN)) { 248cdf63a70SMartin Matuska int r = write(output, prompt, strlen(prompt)); 249cdf63a70SMartin Matuska (void)r; 250cdf63a70SMartin Matuska } 251cdf63a70SMartin Matuska end = buf + bufsiz - 1; 252cdf63a70SMartin Matuska p = buf; 253cdf63a70SMartin Matuska while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') { 254cdf63a70SMartin Matuska if (p < end) { 255cdf63a70SMartin Matuska if ((flags & RPP_SEVENBIT)) 256cdf63a70SMartin Matuska ch &= 0x7f; 257f061a221SMartin Matuska if (isalpha((unsigned char)ch)) { 258cdf63a70SMartin Matuska if ((flags & RPP_FORCELOWER)) 259f061a221SMartin Matuska ch = (char)tolower((unsigned char)ch); 260cdf63a70SMartin Matuska if ((flags & RPP_FORCEUPPER)) 261f061a221SMartin Matuska ch = (char)toupper((unsigned char)ch); 262cdf63a70SMartin Matuska } 263cdf63a70SMartin Matuska *p++ = ch; 264cdf63a70SMartin Matuska } 265cdf63a70SMartin Matuska } 266cdf63a70SMartin Matuska *p = '\0'; 267cdf63a70SMartin Matuska save_errno = errno; 268cdf63a70SMartin Matuska if (!(term.c_lflag & ECHO)) { 269cdf63a70SMartin Matuska int r = write(output, "\n", 1); 270cdf63a70SMartin Matuska (void)r; 271cdf63a70SMartin Matuska } 272cdf63a70SMartin Matuska 273cdf63a70SMartin Matuska /* Restore old terminal settings and signals. */ 274cdf63a70SMartin Matuska if (memcmp(&term, &oterm, sizeof(term)) != 0) { 275fae5c36eSMartin Matuska const int sigttou = signo[SIGTTOU]; 276fae5c36eSMartin Matuska 277fae5c36eSMartin Matuska /* Ignore SIGTTOU generated when we are not the fg pgrp. */ 278cdf63a70SMartin Matuska while (tcsetattr(input, _T_FLUSH, &oterm) == -1 && 279fae5c36eSMartin Matuska errno == EINTR && !signo[SIGTTOU]) 280cdf63a70SMartin Matuska continue; 281fae5c36eSMartin Matuska signo[SIGTTOU] = sigttou; 282cdf63a70SMartin Matuska } 283*e64fe029SMartin Matuska #ifdef HAVE_SIGACTION 284cdf63a70SMartin Matuska (void)sigaction(SIGALRM, &savealrm, NULL); 285cdf63a70SMartin Matuska (void)sigaction(SIGHUP, &savehup, NULL); 286cdf63a70SMartin Matuska (void)sigaction(SIGINT, &saveint, NULL); 287cdf63a70SMartin Matuska (void)sigaction(SIGQUIT, &savequit, NULL); 288cdf63a70SMartin Matuska (void)sigaction(SIGPIPE, &savepipe, NULL); 289cdf63a70SMartin Matuska (void)sigaction(SIGTERM, &saveterm, NULL); 290cdf63a70SMartin Matuska (void)sigaction(SIGTSTP, &savetstp, NULL); 291cdf63a70SMartin Matuska (void)sigaction(SIGTTIN, &savettin, NULL); 292cdf63a70SMartin Matuska (void)sigaction(SIGTTOU, &savettou, NULL); 293*e64fe029SMartin Matuska #endif 294cdf63a70SMartin Matuska if (input != STDIN_FILENO) 295cdf63a70SMartin Matuska (void)close(input); 296cdf63a70SMartin Matuska 297cdf63a70SMartin Matuska /* 298cdf63a70SMartin Matuska * If we were interrupted by a signal, resend it to ourselves 299cdf63a70SMartin Matuska * now that we have restored the signal handlers. 300cdf63a70SMartin Matuska */ 301f061a221SMartin Matuska for (i = 0; i <= MAX_SIGNO; i++) { 302cdf63a70SMartin Matuska if (signo[i]) { 303cdf63a70SMartin Matuska kill(getpid(), i); 304cdf63a70SMartin Matuska switch (i) { 305cdf63a70SMartin Matuska case SIGTSTP: 306cdf63a70SMartin Matuska case SIGTTIN: 307cdf63a70SMartin Matuska case SIGTTOU: 308cdf63a70SMartin Matuska need_restart = 1; 309cdf63a70SMartin Matuska } 310cdf63a70SMartin Matuska } 311cdf63a70SMartin Matuska } 312cdf63a70SMartin Matuska if (need_restart) 313cdf63a70SMartin Matuska goto restart; 314cdf63a70SMartin Matuska 315cdf63a70SMartin Matuska if (save_errno) 316cdf63a70SMartin Matuska errno = save_errno; 317cdf63a70SMartin Matuska return(nr == -1 ? NULL : buf); 318cdf63a70SMartin Matuska } 319cdf63a70SMartin Matuska #endif /* _WIN32 && !__CYGWIN__ */ 320cdf63a70SMartin Matuska #endif /* HAVE_READPASSPHRASE */ 321cdf63a70SMartin Matuska 322cdf63a70SMartin Matuska char * 323cdf63a70SMartin Matuska lafe_readpassphrase(const char *prompt, char *buf, size_t bufsiz) 324cdf63a70SMartin Matuska { 325cdf63a70SMartin Matuska char *p; 326cdf63a70SMartin Matuska 327cdf63a70SMartin Matuska p = readpassphrase(prompt, buf, bufsiz, RPP_ECHO_OFF); 328cdf63a70SMartin Matuska if (p == NULL) { 329cdf63a70SMartin Matuska switch (errno) { 330cdf63a70SMartin Matuska case EINTR: 331cdf63a70SMartin Matuska break; 332cdf63a70SMartin Matuska default: 333cdf63a70SMartin Matuska lafe_errc(1, errno, "Couldn't read passphrase"); 334cdf63a70SMartin Matuska break; 335cdf63a70SMartin Matuska } 336cdf63a70SMartin Matuska } 337cdf63a70SMartin Matuska return (p); 338cdf63a70SMartin Matuska } 339cdf63a70SMartin Matuska 340