~ubuntu-branches/ubuntu/utopic/sshpass/utopic

« back to all changes in this revision

Viewing changes to main.c

  • Committer: Bazaar Package Importer
  • Author(s): Shachar Shemesh
  • Date: 2006-02-28 11:20:23 UTC
  • Revision ID: james.westby@ubuntu.com-20060228112023-3r68wtnkfj553igm
Tags: upstream-1.00
ImportĀ upstreamĀ versionĀ 1.00

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*  This file is part of "sshpass", a tool for batch running password ssh authentication
 
2
 *  Copyright (C) 2006 Lingnu Open Source Consulting Ltd.
 
3
 *
 
4
 *  This program is free software; you can redistribute it and/or modify
 
5
 *  it under the terms of the GNU General Public License as published by
 
6
 *  the Free Software Foundation; either version 2 of the License, or
 
7
 *  (at your option) any later version, provided that it was accepted by
 
8
 *  Lingnu Open Source Consulting Ltd. as an acceptable license for its
 
9
 *  projects. Consult http://www.lingnu.com/licenses.html
 
10
 *
 
11
 *  This program is distributed in the hope that it will be useful,
 
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 *  GNU General Public License for more details.
 
15
 *
 
16
 *  You should have received a copy of the GNU General Public License
 
17
 *  along with this program; if not, write to the Free Software
 
18
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
19
 */
 
20
 
 
21
#if HAVE_CONFIG_H
 
22
#include "config.h"
 
23
#endif
 
24
#define _GNU_SOURCE
 
25
 
 
26
#include <sys/types.h>
 
27
#include <sys/stat.h>
 
28
#include <sys/wait.h>
 
29
#include <sys/ioctl.h>
 
30
#include <sys/select.h>
 
31
 
 
32
#include <unistd.h>
 
33
#include <fcntl.h>
 
34
//#include <asm/ioctls.h>
 
35
 
 
36
#include <stdio.h>
 
37
#include <stdlib.h>
 
38
#include <errno.h>
 
39
#include <string.h>
 
40
 
 
41
int runprogram( int argc, char *argv[] );
 
42
 
 
43
struct {
 
44
    enum { PWT_STDIN, PWT_FILE, PWT_FD, PWT_PASS } pwtype;
 
45
    union {
 
46
        const char *filename;
 
47
        int fd;
 
48
        const char *password;
 
49
    } pwsrc;
 
50
} args;
 
51
 
 
52
static void show_help()
 
53
{
 
54
    printf("Usage: " PACKAGE_NAME " -fdph command parameters\n"
 
55
            "   -f filename   Take password to use from file\n"
 
56
            "   -d number     Use number as file descriptor for getting password\n"
 
57
            "   -p password   Provide password as argument (security unwise)\n"
 
58
            "   -e            Password is passed as env-var \"SSHPASS\"\n"
 
59
            "   With no parameters - password will be taken from stdin\n\n"
 
60
            "   -h            Show help (this screen)\n"
 
61
            "   -V            Print version information\n"
 
62
            "At most one of -f, -d, -p or -e should be used\n");
 
63
}
 
64
 
 
65
// Parse the command line. Fill in the "args" global struct with the results. Return argv offset
 
66
// on success, and a negative number on failure
 
67
static int parse_options( int argc, char *argv[] )
 
68
{
 
69
    int error=0;
 
70
    int opt;
 
71
 
 
72
    // Set the default password source to stdin
 
73
    args.pwtype=PWT_STDIN;
 
74
    args.pwsrc.fd=0;
 
75
 
 
76
#define VIRGIN_PWTYPE if( args.pwtype!=PWT_STDIN ) { \
 
77
    fprintf(stderr, "Conflicting password source\n"); \
 
78
    error=-3; }
 
79
 
 
80
    while( (opt=getopt(argc, argv, "+f:d:p:heV"))!=-1 && error==0 ) {
 
81
        switch( opt ) {
 
82
        case 'f':
 
83
            // Password should come from a file
 
84
            VIRGIN_PWTYPE;
 
85
            
 
86
            args.pwtype=PWT_FILE;
 
87
            args.pwsrc.filename=optarg;
 
88
            break;
 
89
        case 'd':
 
90
            // Password should come from an open file descriptor
 
91
            VIRGIN_PWTYPE;
 
92
 
 
93
            args.pwtype=PWT_FD;
 
94
            args.pwsrc.fd=atoi(optarg);
 
95
            break;
 
96
        case 'p':
 
97
            // Password is given on the command line
 
98
            VIRGIN_PWTYPE;
 
99
 
 
100
            args.pwtype=PWT_PASS;
 
101
            args.pwsrc.password=optarg;
 
102
            break;
 
103
        case 'e':
 
104
            VIRGIN_PWTYPE;
 
105
 
 
106
            args.pwtype=PWT_PASS;
 
107
            args.pwsrc.password=getenv("SSHPASS");
 
108
            break;
 
109
        case '?':
 
110
        case ':':
 
111
            error=-2;
 
112
            break;
 
113
        case 'h':
 
114
            error=-1;
 
115
            break;
 
116
        case 'V':
 
117
            printf("%s (C) 2006 Lingnu Open Source Consulting Ltd.\n"
 
118
                    "This program is free software, and can be distributed under the terms of the GPL\n"
 
119
                    "See the COPYING file for more information.\n", PACKAGE_STRING );
 
120
            exit(0);
 
121
            break;
 
122
        }
 
123
    }
 
124
 
 
125
    if( error==0 )
 
126
        return optind;
 
127
    else
 
128
        return error;
 
129
}
 
130
 
 
131
int main( int argc, char *argv[] )
 
132
{
 
133
    int opt_offset=parse_options( argc, argv );
 
134
 
 
135
    if( opt_offset<0 ) {
 
136
        // There was some error
 
137
        show_help();
 
138
 
 
139
        if( opt_offset==-1 )
 
140
            return 0;
 
141
        else
 
142
            return -opt_offset;
 
143
    }
 
144
 
 
145
    return runprogram( argc-opt_offset, argv+opt_offset );
 
146
}
 
147
 
 
148
int handleoutput( int fd );
 
149
 
 
150
int runprogram( int argc, char *argv[] )
 
151
{
 
152
    // Create a pseudo terminal for our process
 
153
    int masterpt=getpt();
 
154
 
 
155
    if( masterpt==-1 ) {
 
156
        perror("Failed to get a pseudo terminal");
 
157
 
 
158
        return 1;
 
159
    }
 
160
 
 
161
    if( grantpt( masterpt )!=0 ) {
 
162
        perror("Failed to change pseudo terminal's permission");
 
163
 
 
164
        return 1;
 
165
    }
 
166
    if( unlockpt( masterpt )!=0 ) {
 
167
        perror("Failed to unlock pseudo terminal");
 
168
 
 
169
        return 1;
 
170
    }
 
171
 
 
172
    int childpid=fork();
 
173
    if( childpid==0 ) {
 
174
        // Child
 
175
 
 
176
        // Detach us from the current TTY
 
177
        setsid();
 
178
        
 
179
        const char *name=ptsname(masterpt);
 
180
        int slavept=open(name, O_RDWR );
 
181
        //fprintf(stderr, "Opened %s with fd %d\n", name, slavept);
 
182
        close( masterpt );
 
183
 
 
184
        char **new_argv=malloc(sizeof(char *)*(argc+1));
 
185
 
 
186
        int i;
 
187
 
 
188
        for( i=0; i<argc; ++i ) {
 
189
            new_argv[i]=argv[i];
 
190
        }
 
191
 
 
192
        new_argv[i]=NULL;
 
193
 
 
194
        execvp( new_argv[0], new_argv );
 
195
 
 
196
        perror("sshpass: Failed to run command");
 
197
 
 
198
        exit(errno);
 
199
    } else if( childpid<0 ) {
 
200
        perror("sshpass: Failed to create child process");
 
201
 
 
202
        return errno;
 
203
    }
 
204
        
 
205
    // We are the parent
 
206
    int status=0;
 
207
    int terminate=0;
 
208
    pid_t wait_id;
 
209
    do {
 
210
        if( !terminate ) {
 
211
            fd_set readfd;
 
212
 
 
213
            FD_ZERO(&readfd);
 
214
            FD_SET(masterpt, &readfd);
 
215
 
 
216
            int selret=select( masterpt+1, &readfd, NULL, NULL, NULL );
 
217
 
 
218
            if( selret>0 ) {
 
219
                if( FD_ISSET( masterpt, &readfd ) ) {
 
220
                    if( handleoutput( masterpt ) ) {
 
221
                        // Authentication failed - need to abort
 
222
                        close( masterpt ); // Signal ssh that it's controlling TTY is now closed
 
223
                        terminate=255; // This is what openssh returns on authentication errors
 
224
                    }
 
225
                }
 
226
            }
 
227
            wait_id=waitpid( childpid, &status, WNOHANG );
 
228
        } else {
 
229
            wait_id=waitpid( childpid, &status, 0 );
 
230
        }
 
231
    } while( wait_id==0 || (!WIFEXITED( status ) && !WIFSIGNALED( status )) );
 
232
 
 
233
    if( terminate!=0 )
 
234
        return terminate;
 
235
    else if( WIFEXITED( status ) )
 
236
        return WEXITSTATUS(status);
 
237
    else
 
238
        return 255;
 
239
}
 
240
 
 
241
int match( const char *reference, const char *buffer, ssize_t bufsize, int state );
 
242
void write_pass( int fd );
 
243
 
 
244
int handleoutput( int fd )
 
245
{
 
246
    // We are looking for the string
 
247
    static int prevmatch=0; // If the "password" prompt is repeated, we have the wrong password.
 
248
    static int state;
 
249
    static const char compare[]="assword:";
 
250
    char buffer[40];
 
251
    int ret=0;
 
252
 
 
253
    int numread=read(fd, buffer, sizeof(buffer) );
 
254
 
 
255
    state=match( compare, buffer, numread, state );
 
256
 
 
257
    if( compare[state]=='\0' ) {
 
258
        if( !prevmatch ) {
 
259
            write_pass( fd );
 
260
            state=0;
 
261
            prevmatch=1;
 
262
        } else {
 
263
            // Wrong password - terminate with proper error code
 
264
            ret=1;
 
265
        }
 
266
    }
 
267
 
 
268
 
 
269
    return ret;
 
270
}
 
271
 
 
272
int match( const char *reference, const char *buffer, ssize_t bufsize, int state )
 
273
{
 
274
    // This is a highly simplisic implementation. It's good enough for matching "Password: ", though.
 
275
    int i;
 
276
    for( i=0;reference[state]!='\0' && i<bufsize; ++i ) {
 
277
        if( reference[state]==buffer[i] )
 
278
            state++;
 
279
        else {
 
280
            state=0;
 
281
            if( reference[state]==buffer[i] )
 
282
                state++;
 
283
        }
 
284
    }
 
285
 
 
286
    return state;
 
287
}
 
288
 
 
289
void write_pass_fd( int srcfd, int dstfd );
 
290
 
 
291
void write_pass( int fd )
 
292
{
 
293
    switch( args.pwtype ) {
 
294
    case PWT_STDIN:
 
295
        write_pass_fd( STDIN_FILENO, fd );
 
296
        break;
 
297
    case PWT_FD:
 
298
        write_pass_fd( args.pwsrc.fd, fd );
 
299
        break;
 
300
    case PWT_FILE:
 
301
        {
 
302
            int srcfd=open( args.pwsrc.filename, O_RDONLY );
 
303
            if( srcfd!=-1 ) {
 
304
                write_pass_fd( srcfd, fd );
 
305
                close( srcfd );
 
306
            }
 
307
        }
 
308
        break;
 
309
    case PWT_PASS:
 
310
        write( fd, args.pwsrc.password, strlen( args.pwsrc.password ) );
 
311
        write( fd, "\n", 1 );
 
312
        break;
 
313
    }
 
314
}
 
315
 
 
316
void write_pass_fd( int srcfd, int dstfd )
 
317
{
 
318
 
 
319
    int done=0;
 
320
 
 
321
    while( !done ) {
 
322
        char buffer[40];
 
323
        int i;
 
324
        int numread=read( srcfd, buffer, sizeof(buffer) );
 
325
        done=(numread<1);
 
326
        for( i=0; i<numread && !done; ++i ) {
 
327
            if( buffer[i]!='\n' )
 
328
                write( dstfd, buffer+i, 1 );
 
329
            else
 
330
                done=1;
 
331
        }
 
332
    }
 
333
 
 
334
    write( dstfd, "\n", 1 );
 
335
}