1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * Copyright 2023 OmniOS Community Edition (OmniOSce) Association. 26 */ 27 28 /* 29 * This file has all of the PAM related code for sys-suspend. It is 30 * part of it's own file, as these could be part of some bigger item 31 * that can handle generic PAM facilities (certainly the getinput() 32 * function could be in a common library). However, as that does not 33 * yet exist, we replicate it here so we can get the job done. 34 */ 35 36 #define __EXTENSIONS__ /* to expose flockfile and friends in stdio.h */ 37 #include <errno.h> 38 #include <libgen.h> 39 #include <malloc.h> 40 #include <signal.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <strings.h> 44 #include <stropts.h> 45 #include <unistd.h> 46 #include <termio.h> 47 48 #include <security/pam_appl.h> 49 50 static int ctl_c; /* was the conversation interrupted? */ 51 52 /* ARGSUSED 1 */ 53 static void 54 interrupt(int x) 55 { 56 ctl_c = 1; 57 } 58 59 /* 60 * getinput -- read user input from stdin abort on ^C 61 * 62 * Entry noecho == TRUE, don't echo input. 63 * 64 * Exit User's input. 65 * If interrupted, send SIGINT to caller for processing. 66 */ 67 static char * 68 getinput(int noecho) 69 { 70 struct termio tty; 71 unsigned short tty_flags = 0; 72 char input[PAM_MAX_RESP_SIZE + 1]; 73 int c; 74 int i = 0; 75 void (*sig)(int); 76 77 ctl_c = 0; 78 sig = signal(SIGINT, interrupt); 79 if (noecho) { 80 (void) ioctl(fileno(stdin), TCGETA, &tty); 81 tty_flags = tty.c_lflag; 82 tty.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); 83 (void) ioctl(fileno(stdin), TCSETAF, &tty); 84 } 85 /* go to end, but don't overflow PAM_MAX_RESP_SIZE */ 86 flockfile(stdin); 87 while (ctl_c == 0 && 88 (c = getchar_unlocked()) != '\n' && 89 c != '\r' && 90 c != EOF) { 91 if (i < PAM_MAX_RESP_SIZE) { 92 input[i++] = (char)c; 93 } 94 } 95 funlockfile(stdin); 96 input[i] = '\0'; 97 if (noecho) { 98 tty.c_lflag = tty_flags; 99 (void) ioctl(fileno(stdin), TCSETAW, &tty); 100 (void) fputc('\n', stdout); 101 } 102 (void) signal(SIGINT, sig); 103 if (ctl_c == 1) 104 (void) kill(getpid(), SIGINT); 105 106 return (strdup(input)); 107 } 108 109 /* 110 * Service modules don't clean up responses if an error is returned. 111 * Free responses here. 112 */ 113 static void 114 free_resp(int num_msg, struct pam_response *pr) 115 { 116 int i; 117 struct pam_response *r = pr; 118 119 if (pr == NULL) 120 return; 121 122 for (i = 0; i < num_msg; i++, r++) { 123 124 if (r->resp) { 125 /* clear before freeing -- may be a password */ 126 bzero(r->resp, strlen(r->resp)); 127 free(r->resp); 128 r->resp = NULL; 129 } 130 } 131 free(pr); 132 } 133 134 int 135 pam_tty_conv(int num_msg, const struct pam_message **mess, 136 struct pam_response **resp, void *my_data) 137 { 138 const struct pam_message *m = *mess; 139 struct pam_response *r = calloc(num_msg, sizeof (struct pam_response)); 140 int i; 141 142 if (num_msg >= PAM_MAX_NUM_MSG) { 143 (void) fprintf(stderr, "too many messages %d >= %d\n", 144 num_msg, PAM_MAX_NUM_MSG); 145 free(r); 146 *resp = NULL; 147 return (PAM_CONV_ERR); 148 } 149 150 /* Talk it out */ 151 *resp = r; 152 for (i = 0; i < num_msg; i++) { 153 int echo_off; 154 155 /* bad message from service module */ 156 if (m->msg == NULL) { 157 (void) fprintf(stderr, "message[%d]: %d/NULL\n", 158 i, m->msg_style); 159 goto err; 160 } 161 162 /* 163 * fix up final newline: 164 * removed for prompts 165 * added back for messages 166 */ 167 if (m->msg[strlen(m->msg)] == '\n') 168 m->msg[strlen(m->msg)] = '\0'; 169 170 r->resp = NULL; 171 r->resp_retcode = 0; 172 echo_off = 0; 173 switch (m->msg_style) { 174 175 case PAM_PROMPT_ECHO_OFF: 176 echo_off = 1; 177 /*FALLTHROUGH*/ 178 179 case PAM_PROMPT_ECHO_ON: 180 (void) fputs(m->msg, stdout); 181 182 r->resp = getinput(echo_off); 183 break; 184 185 case PAM_ERROR_MSG: 186 (void) fputs(m->msg, stderr); 187 (void) fputc('\n', stderr); 188 break; 189 190 case PAM_TEXT_INFO: 191 (void) fputs(m->msg, stdout); 192 (void) fputc('\n', stdout); 193 break; 194 195 default: 196 (void) fprintf(stderr, "message[%d]: unknown type " 197 "%d/val=\"%s\"\n", 198 i, m->msg_style, m->msg); 199 /* error, service module won't clean up */ 200 goto err; 201 } 202 if (errno == EINTR) 203 goto err; 204 205 /* next message/response */ 206 m++; 207 r++; 208 } 209 return (PAM_SUCCESS); 210 211 err: 212 free_resp(i, r); 213 *resp = NULL; 214 return (PAM_CONV_ERR); 215 } 216