login_skey.c revision 1.11
1/* $OpenBSD: login_skey.c,v 1.11 2002/09/06 18:45:07 deraadt Exp $ */ 2 3/*- 4 * Copyright (c) 1995 Berkeley Software Design, Inc. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by Berkeley Software Design, 17 * Inc. 18 * 4. The name of Berkeley Software Design, Inc. may not be used to endorse 19 * or promote products derived from this software without specific prior 20 * written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * BSDI $From: login_skey.c,v 1.3 1996/09/04 05:24:56 prb Exp $ 35 */ 36#include <sys/types.h> 37#include <sys/param.h> 38#include <sys/stat.h> 39#include <sys/time.h> 40#include <sys/resource.h> 41 42#include <ctype.h> 43#include <errno.h> 44#include <fcntl.h> 45#include <paths.h> 46#include <pwd.h> 47#include <readpassphrase.h> 48#include <signal.h> 49#include <stdio.h> 50#include <stdlib.h> 51#include <string.h> 52#include <syslog.h> 53#include <unistd.h> 54 55#include <login_cap.h> 56#include <bsd_auth.h> 57#include <sha1.h> 58#include <skey.h> 59 60void quit(int); 61void suspend(int); 62 63volatile sig_atomic_t resumed; 64struct skey skey; 65 66int 67main(int argc, char *argv[]) 68{ 69 FILE *back = NULL; 70 char *class = 0; 71 char *username = 0; 72 char skeyprompt[SKEY_MAX_CHALLENGE+17]; 73 char passbuf[SKEY_MAX_PW_LEN+1]; 74 int c, haskey; 75 int mode = 0; 76 77 skeyprompt[0] = '\0'; 78 79 (void)signal(SIGINT, quit); 80 (void)signal(SIGQUIT, quit); 81 (void)signal(SIGALRM, quit); 82 (void)signal(SIGTSTP, suspend); 83 (void)setpriority(PRIO_PROCESS, 0, 0); 84 85 openlog(NULL, LOG_ODELAY, LOG_AUTH); 86 87 while ((c = getopt(argc, argv, "ds:v:")) != -1) 88 switch (c) { 89 case 'd': /* to remain undocumented */ 90 back = stdout; 91 break; 92 case 'v': 93 break; 94 case 's': /* service */ 95 if (strcmp(optarg, "login") == 0) 96 mode = 0; 97 else if (strcmp(optarg, "challenge") == 0) 98 mode = 1; 99 else if (strcmp(optarg, "response") == 0) 100 mode = 2; 101 else { 102 syslog(LOG_ERR, "%s: invalid service", optarg); 103 exit(1); 104 } 105 break; 106 default: 107 syslog(LOG_ERR, "usage error"); 108 exit(1); 109 } 110 111 switch (argc - optind) { 112 case 2: 113 class = argv[optind + 1]; 114 case 1: 115 username = argv[optind]; 116 break; 117 default: 118 syslog(LOG_ERR, "usage error"); 119 exit(1); 120 } 121 122 123 if (back == NULL && (back = fdopen(3, "r+")) == NULL) { 124 syslog(LOG_ERR, "reopening back channel: %m"); 125 exit(1); 126 } 127 128 if (mode == 2) { 129 mode = 0; 130 c = -1; 131 /* XXX - redo these loops! */ 132 while (++c < sizeof(skeyprompt) && 133 read(3, &skeyprompt[c], 1) == 1) { 134 if (skeyprompt[c] == '\0') { 135 mode++; 136 break; 137 } 138 } 139 if (mode == 1) { 140 c = -1; 141 while (++c < sizeof(passbuf) && 142 read(3, &passbuf[c], 1) == 1) { 143 if (passbuf[c] == '\0') { 144 mode++; 145 break; 146 } 147 } 148 } 149 if (mode < 2) { 150 syslog(LOG_ERR, "protocol error on back channel"); 151 exit(1); 152 } 153 /* 154 * Sigh. S/Key really is a stateful protocol. 155 * We must assume that a user will only try to 156 * authenticate one at a time and that this call to 157 * skeychallenge will produce the same results as 158 * the call to skeychallenge when mode was 1. 159 * 160 * Furthermore, RFC2289 requires that an entry be 161 * locked against a partial guess race which is 162 * simply not possible if the calling program queries 163 * the user for the passphrase itself. Oh well. 164 */ 165 haskey = (skeychallenge(&skey, username, skeyprompt) == 0); 166 } else { 167 /* 168 * Attempt an S/Key challenge. 169 * The OpenBSD skeychallenge() will always fill in a 170 * challenge, even if it has to cons one up. 171 */ 172 haskey = (skeychallenge(&skey, username, skeyprompt) == 0); 173 strlcat(skeyprompt, "\nS/Key Password: ", sizeof skeyprompt); 174 if (mode == 1) { 175 fprintf(back, BI_VALUE " challenge %s\n", 176 auth_mkvalue(skeyprompt)); 177 fprintf(back, BI_CHALLENGE "\n"); 178 exit(0); 179 } 180 181 /* Time out getting passphrase after 2 minutes to avoid a DoS */ 182 if (haskey) 183 alarm(120); 184 resumed = 0; 185 readpassphrase(skeyprompt, passbuf, sizeof(passbuf), 0); 186 if (passbuf[0] == '\0') 187 readpassphrase("S/Key Password [echo on]: ", 188 passbuf, sizeof(passbuf), RPP_ECHO_ON); 189 alarm(0); 190 if (resumed) { 191 /* 192 * We were suspended by the user. Our lock is 193 * no longer valid so we must regain it so 194 * an attacker cannot do a partial guess of 195 * an S/Key response already in progress. 196 */ 197 haskey = (skeylookup(&skey, username) == 0); 198 } 199 } 200 201 /* 202 * Ignore keyboard interupt/suspend during database update. 203 */ 204 signal(SIGINT, SIG_IGN); 205 signal(SIGQUIT, SIG_IGN); 206 signal(SIGTSTP, SIG_IGN); 207 208 if (haskey && skeyverify(&skey, passbuf) == 0) { 209 if (mode == 0) { 210 if (skey.n <= 1) 211 printf("Warning! You MUST change your " 212 "S/Key password now!\n"); 213 else if (skey.n < 5) 214 printf("Warning! Change S/Key password soon\n"); 215 } 216 fprintf(back, BI_AUTH "\n"); 217 fprintf(back, BI_SECURE "\n"); 218 exit(0); 219 } 220 fprintf(back, BI_REJECT "\n"); 221 exit(1); 222} 223 224void 225quit(int signo) 226{ 227 228 _exit(1); 229} 230 231void 232suspend(int signo) 233{ 234 sigset_t nset; 235 int save_errno = errno; 236 237 /* 238 * Unlock the skey record so we don't sleep holding the lock. 239 * Unblock SIGTSTP, set it to the default action and then 240 * resend it so we are suspended properly. 241 * When we resume, reblock SIGTSTP, reset the signal handler, 242 * set a flag and restore errno. 243 */ 244 alarm(0); 245 skey_unlock(&skey); 246 (void)signal(signo, SIG_DFL); 247 (void)sigemptyset(&nset); 248 (void)sigaddset(&nset, signo); 249 (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); 250 (void)kill(getpid(), signo); 251 (void)sigprocmask(SIG_BLOCK, &nset, NULL); 252 (void)signal(signo, suspend); 253 resumed = 1; 254 errno = save_errno; 255} 256