login_skey.c revision 1.8
1/* $OpenBSD: login_skey.c,v 1.8 2002/05/29 18:57:06 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(argc, argv) 68 int argc; 69 char **argv; 70{ 71 FILE *back = NULL; 72 char *class = 0; 73 char *username = 0; 74 char skeyprompt[SKEY_MAX_CHALLENGE+17]; 75 char passbuf[SKEY_MAX_PW_LEN+1]; 76 int c, haskey; 77 int mode = 0; 78 79 skeyprompt[0] = '\0'; 80 81 (void)signal(SIGINT, quit); 82 (void)signal(SIGQUIT, quit); 83 (void)signal(SIGALRM, quit); 84 (void)signal(SIGTSTP, suspend); 85 (void)setpriority(PRIO_PROCESS, 0, 0); 86 87 openlog(NULL, LOG_ODELAY, LOG_AUTH); 88 89 while ((c = getopt(argc, argv, "ds:v:")) != -1) 90 switch(c) { 91 case 'd': /* to remain undocumented */ 92 back = stdout; 93 break; 94 case 'v': 95 break; 96 case 's': /* service */ 97 if (strcmp(optarg, "login") == 0) 98 mode = 0; 99 else if (strcmp(optarg, "challenge") == 0) 100 mode = 1; 101 else if (strcmp(optarg, "response") == 0) 102 mode = 2; 103 else { 104 syslog(LOG_ERR, "%s: invalid service", optarg); 105 exit(1); 106 } 107 break; 108 default: 109 syslog(LOG_ERR, "usage error"); 110 exit(1); 111 } 112 113 switch(argc - optind) { 114 case 2: 115 class = argv[optind + 1]; 116 case 1: 117 username = argv[optind]; 118 break; 119 default: 120 syslog(LOG_ERR, "usage error"); 121 exit(1); 122 } 123 124 125 if (back == NULL && (back = fdopen(3, "r+")) == NULL) { 126 syslog(LOG_ERR, "reopening back channel: %m"); 127 exit(1); 128 } 129 130 if (mode == 2) { 131 mode = 0; 132 c = -1; 133 /* XXX - redo these loops! */ 134 while (++c < sizeof(skeyprompt) && 135 read(3, &skeyprompt[c], 1) == 1) { 136 if (skeyprompt[c] == '\0') { 137 mode++; 138 break; 139 } 140 } 141 if (mode == 1) { 142 c = -1; 143 while (++c < sizeof(passbuf) && 144 read(3, &passbuf[c], 1) == 1) { 145 if (passbuf[c] == '\0') { 146 mode++; 147 break; 148 } 149 } 150 } 151 if (mode < 2) { 152 syslog(LOG_ERR, "protocol error on back channel"); 153 exit(1); 154 } 155 /* 156 * Sigh. S/Key really is a stateful protocol. 157 * We must assume that a user will only try to 158 * authenticate one at a time and that this call to 159 * skeychallenge will produce the same results as 160 * the call to skeychallenge when mode was 1. 161 * 162 * Furthermore, RFC2289 requires that an entry be 163 * locked against a partial guess race which is 164 * simply not possible if the calling program queries 165 * the user for the passphrase itself. Oh well. 166 */ 167 haskey = (skeychallenge(&skey, username, skeyprompt) == 0); 168 } else { 169 /* 170 * Attempt an S/Key challenge. 171 * The OpenBSD skeychallenge() will always fill in a 172 * challenge, even if it has to cons one up. 173 */ 174 haskey = (skeychallenge(&skey, username, skeyprompt) == 0); 175 strlcat(skeyprompt, "\nS/Key Password: ", sizeof skeyprompt); 176 if (mode == 1) { 177 fprintf(back, BI_VALUE " challenge %s\n", 178 auth_mkvalue(skeyprompt)); 179 fprintf(back, BI_CHALLENGE "\n"); 180 exit(0); 181 } 182 183 /* Time out getting passphrase after 2 minutes to avoid a DoS */ 184 if (haskey) 185 alarm(120); 186 resumed = 0; 187 readpassphrase(skeyprompt, passbuf, sizeof(passbuf), 0); 188 if (passbuf[0] == '\0') 189 readpassphrase("S/Key Password [echo on]: ", 190 passbuf, sizeof(passbuf), RPP_ECHO_ON); 191 alarm(0); 192 if (resumed) { 193 /* 194 * We were suspended by the user. Our lock is 195 * no longer valid so we must regain it so 196 * an attacker cannot do a partial guess of 197 * an S/Key response already in progress. 198 */ 199 haskey = (skeylookup(&skey, username) == 0); 200 } 201 } 202 203 /* 204 * Ignore keyboard interupt/suspend during database update. 205 */ 206 signal(SIGINT, SIG_IGN); 207 signal(SIGQUIT, SIG_IGN); 208 signal(SIGTSTP, SIG_IGN); 209 210 if (haskey && skeyverify(&skey, passbuf) == 0) { 211 if (mode == 0) { 212 if (skey.n <= 1) 213 printf("Warning! You MUST change your " 214 "S/Key password now!\n"); 215 else if (skey.n < 5) 216 printf("Warning! Change S/Key password soon\n"); 217 } 218 fprintf(back, BI_AUTH "\n"); 219 fprintf(back, BI_SECURE "\n"); 220 exit(0); 221 } 222 fprintf(back, BI_REJECT "\n"); 223 exit(1); 224} 225 226void 227quit(signo) 228 int signo; 229{ 230 231 _exit(1); 232} 233 234void 235suspend(signo) 236 int signo; 237{ 238 sigset_t nset; 239 int save_errno = errno; 240 241 /* 242 * Unlock the skey record so we don't sleep holding the lock. 243 * Unblock SIGTSTP, set it to the default action and then 244 * resend it so we are suspended properly. 245 * When we resume, reblock SIGTSTP, reset the signal handler, 246 * set a flag and restore errno. 247 */ 248 alarm(0); 249 skey_unlock(&skey); 250 (void)signal(signo, SIG_DFL); 251 (void)sigemptyset(&nset); 252 (void)sigaddset(&nset, signo); 253 (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); 254 (void)kill(getpid(), signo); 255 (void)sigprocmask(SIG_BLOCK, &nset, NULL); 256 (void)signal(signo, suspend); 257 resumed = 1; 258 errno = save_errno; 259} 260