summaryrefslogtreecommitdiff
path: root/pppd/plugins/passprompt.c
blob: 06d9ff2197c9f89a080f4186d2c57c792b67b916 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
 * passprompt.c - pppd plugin to invoke an external PAP password prompter
 *
 * Copyright 1999 Paul Mackerras, Alan Curry.
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version
 *  2 of the License, or (at your option) any later version.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <syslog.h>
#include "pppd.h"

char pppd_version[] = VERSION;

static char promptprog[PATH_MAX+1];
static int promptprog_refused = 0;

static option_t options[] = {
    { "promptprog", o_string, promptprog,
      "External PAP password prompting program",
      OPT_STATIC, NULL, PATH_MAX },
    { NULL }
};

static int promptpass(char *user, char *passwd)
{
    int p[2];
    pid_t kid;
    int readgood, wstat, ret;
    ssize_t red;

    if (promptprog_refused || promptprog[0] == 0 || access(promptprog, X_OK) < 0)
	return -1;	/* sorry, can't help */

    if (!passwd)
	return 1;

    if (pipe(p)) {
	warn("Can't make a pipe for %s", promptprog);
	return 0;
    }
    if ((kid = fork()) == (pid_t) -1) {
	warn("Can't fork to run %s", promptprog);
	close(p[0]);
	close(p[1]);
	return 0;
    }
    if (!kid) {
	/* we are the child, exec the program */
	char *argv[5], fdstr[32];
	sys_close();
	closelog();
	close(p[0]);
	ret = seteuid(getuid());
	if (ret != 0) {
		warn("Couldn't set effective user id");
	}
	ret = setegid(getgid());
	if (ret != 0) {
		warn("Couldn't set effective user id");
	}
	argv[0] = promptprog;
	argv[1] = user;
	argv[2] = remote_name;
	sprintf(fdstr, "%d", p[1]);
	argv[3] = fdstr;
	argv[4] = 0;
	execv(*argv, argv);
	_exit(127);
    }

    /* we are the parent, read the password from the pipe */
    close(p[1]);
    readgood = 0;
    do {
	red = read(p[0], passwd + readgood, MAXSECRETLEN-1 - readgood);
	if (red == 0)
	    break;
	if (red < 0) {
	    if (errno == EINTR && !got_sigterm)
		continue;
	    error("Can't read secret from %s: %m", promptprog);
	    readgood = -1;
	    break;
	}
	readgood += red;
    } while (readgood < MAXSECRETLEN - 1);
    close(p[0]);

    /* now wait for child to exit */
    while (waitpid(kid, &wstat, 0) < 0) {
	if (errno != EINTR || got_sigterm) {
	    warn("error waiting for %s: %m", promptprog);
	    break;
	}
    }

    if (readgood < 0)
	return 0;
    passwd[readgood] = 0;
    if (!WIFEXITED(wstat))
	warn("%s terminated abnormally", promptprog);
    if (WEXITSTATUS(wstat)) {
	    warn("%s exited with code %d", promptprog, WEXITSTATUS(wstat));
	    /* code when cancel was hit in the prompt prog */
	    if (WEXITSTATUS(wstat) == 128) {
	        promptprog_refused = 1;
	    }
	    return -1;
    }
    return 1;
}

void plugin_init(void)
{
    add_options(options);
    pap_passwd_hook = promptpass;
#ifdef PPP_WITH_EAPTLS
    eaptls_passwd_hook = promptpass;
#endif
}