1    	/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2    	/* NetworkManager -- Network link manager
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.
8    	 *
9    	 * This program is distributed in the hope that it will be useful,
10   	 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   	 * GNU General Public License for more details.
13   	 *
14   	 * You should have received a copy of the GNU General Public License along
15   	 * with this program; if not, write to the Free Software Foundation, Inc.,
16   	 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17   	 *
18   	 * Copyright (C) 2008 Novell, Inc.
19   	 * Copyright (C) 2008 Red Hat, Inc.
20   	 */
21   	
22   	#include <string.h>
23   	#include <pppd/pppd.h>
24   	#include <pppd/fsm.h>
25   	#include <pppd/ipcp.h>
26   	#include <sys/socket.h>
27   	#include <netinet/in.h>
28   	#include <arpa/inet.h>
29   	#include <glib.h>
30   	#include <glib-object.h>
31   	#include <dbus/dbus-glib.h>
32   	
33   	#include "NetworkManager.h"
34   	#include "nm-pppd-plugin.h"
35   	#include "nm-ppp-status.h"
36   	#include "nm-dbus-glib-types.h"
37   	
38   	int plugin_init (void);
39   	
40   	char pppd_version[] = VERSION;
41   	
42   	static DBusGProxy *proxy = NULL;
43   	
44   	static void
45   	nm_phasechange (void *data, int arg)
46   	{
47   		NMPPPStatus ppp_status = NM_PPP_STATUS_UNKNOWN;
48   		char *ppp_phase;
49   	
50   		g_return_if_fail (DBUS_IS_G_PROXY (proxy));
51   	
52   		switch (arg) {
53   		case PHASE_DEAD:
54   			ppp_status = NM_PPP_STATUS_DEAD;
55   			ppp_phase = "dead";
56   			break;
57   		case PHASE_INITIALIZE:
58   			ppp_status = NM_PPP_STATUS_INITIALIZE;
59   			ppp_phase = "initialize";
60   			break;
61   		case PHASE_SERIALCONN:
62   			ppp_status = NM_PPP_STATUS_SERIALCONN;
63   			ppp_phase = "serial connection";
64   			break;
65   		case PHASE_DORMANT:
66   			ppp_status = NM_PPP_STATUS_DORMANT;
67   			ppp_phase = "dormant";
68   			break;
69   		case PHASE_ESTABLISH:
70   			ppp_status = NM_PPP_STATUS_ESTABLISH;
71   			ppp_phase = "establish";
72   			break;
73   		case PHASE_AUTHENTICATE:
74   			ppp_status = NM_PPP_STATUS_AUTHENTICATE;
75   			ppp_phase = "authenticate";
76   			break;
77   		case PHASE_CALLBACK:
78   			ppp_status = NM_PPP_STATUS_CALLBACK;
79   			ppp_phase = "callback";
80   			break;
81   		case PHASE_NETWORK:
82   			ppp_status = NM_PPP_STATUS_NETWORK;
83   			ppp_phase = "network";
84   			break;
85   		case PHASE_RUNNING:
86   			ppp_status = NM_PPP_STATUS_RUNNING;
87   			ppp_phase = "running";
88   			break;
89   		case PHASE_TERMINATE:
90   			ppp_status = NM_PPP_STATUS_TERMINATE;
91   			ppp_phase = "terminate";
92   			break;
93   		case PHASE_DISCONNECT:
94   			ppp_status = NM_PPP_STATUS_DISCONNECT;
95   			ppp_phase = "disconnect";
96   			break;
97   		case PHASE_HOLDOFF:
98   			ppp_status = NM_PPP_STATUS_HOLDOFF;
99   			ppp_phase = "holdoff";
100  			break;
101  		case PHASE_MASTER:
102  			ppp_status = NM_PPP_STATUS_MASTER;
103  			ppp_phase = "master";
104  			break;
105  	
106  		default:
107  			ppp_phase = "unknown";
108  			break;
109  		}
110  	
111  		g_message ("nm-ppp-plugin: (%s): status %d / phase '%s'",
112  		           __func__,
113  		           ppp_status,
114  		           ppp_phase);
115  	
116  		if (ppp_status != NM_PPP_STATUS_UNKNOWN) {
117  			dbus_g_proxy_call_no_reply (proxy, "SetState",
118  			                            G_TYPE_UINT, ppp_status, G_TYPE_INVALID,
119  			                            G_TYPE_INVALID);
120  		}
121  	}
122  	
123  	static GValue *
124  	str_to_gvalue (const char *str)
125  	{
126  		GValue *val;
127  	
128  		val = g_slice_new0 (GValue);
129  		g_value_init (val, G_TYPE_STRING);
130  		g_value_set_string (val, str);
131  	
132  		return val;
133  	}
134  	
135  	static GValue *
136  	uint_to_gvalue (guint32 i)
137  	{
138  		GValue *val;
139  	
140  		val = g_slice_new0 (GValue);
141  		g_value_init (val, G_TYPE_UINT);
142  		g_value_set_uint (val, i);
143  	
144  		return val;
145  	}
146  	
147  	static void
148  	value_destroy (gpointer data)
149  	{
150  		GValue *val = (GValue *) data;
151  	
152  		g_value_unset (val);
153  		g_slice_free (GValue, val);
154  	}
155  	
156  	static void
157  	nm_ip_up (void *data, int arg)
158  	{
159  		ipcp_options opts = ipcp_gotoptions[0];
160  		ipcp_options peer_opts = ipcp_hisoptions[0];
161  		GHashTable *hash;
162  		GArray *array;
163  		GValue *val;
164  		guint32 pppd_made_up_address = htonl (0x0a404040 + ifunit);
165  	
166  		g_return_if_fail (DBUS_IS_G_PROXY (proxy));
167  	
168  		g_message ("nm-ppp-plugin: (%s): ip-up event", __func__);
169  	
170  		if (!opts.ouraddr) {
171  			g_warning ("nm-ppp-plugin: (%s): didn't receive an internal IP from pppd!", __func__);
172  			nm_phasechange (NULL, PHASE_DEAD);
173  			return;
174  		}
175  	
176  		hash = g_hash_table_new_full (g_str_hash, g_str_equal,
177  								NULL, value_destroy);
178  	
179  		g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_INTERFACE, 
180  						 str_to_gvalue (ifname));
181  	
182  		g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_ADDRESS, 
183  						 uint_to_gvalue (opts.ouraddr));
184  	
185  		/* Prefer the peer options remote address first, _unless_ pppd made the
186  		 * address up, at which point prefer the local options remote address,
187  		 * and if that's not right, use the made-up address as a last resort.
188  		 */
189  		if (peer_opts.hisaddr && (peer_opts.hisaddr != pppd_made_up_address)) {
190  			g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_GATEWAY, 
191  			                     uint_to_gvalue (peer_opts.hisaddr));
192  		} else if (opts.hisaddr) {
193  			g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_GATEWAY, 
194  			                     uint_to_gvalue (opts.hisaddr));
195  		} else if (peer_opts.hisaddr == pppd_made_up_address) {
196  			/* As a last resort, use the made-up address */
197  			g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_GATEWAY, 
198  			                     uint_to_gvalue (peer_opts.hisaddr));
199  		}
200  	
201  		g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_PREFIX, uint_to_gvalue (32));
202  	
203  		if (opts.dnsaddr[0] || opts.dnsaddr[1]) {
204  			array = g_array_new (FALSE, FALSE, sizeof (guint32));
205  	
206  			if (opts.dnsaddr[0])
207  				g_array_append_val (array, opts.dnsaddr[0]);
208  			if (opts.dnsaddr[1])
209  				g_array_append_val (array, opts.dnsaddr[1]);
210  	
211  			val = g_slice_new0 (GValue);
212  			g_value_init (val, DBUS_TYPE_G_UINT_ARRAY);
213  			g_value_set_boxed (val, array);
214  	
215  			g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_DNS, val);
216  		}
217  	
218  		if (opts.winsaddr[0] || opts.winsaddr[1]) {
219  			array = g_array_new (FALSE, FALSE, sizeof (guint32));
220  	
221  			if (opts.winsaddr[0])
222  				g_array_append_val (array, opts.winsaddr[0]);
223  			if (opts.winsaddr[1])
224  				g_array_append_val (array, opts.winsaddr[1]);
225  	
226  			val = g_slice_new0 (GValue);
227  			g_value_init (val, DBUS_TYPE_G_UINT_ARRAY);
228  			g_value_set_boxed (val, array);
229  	
230  			g_hash_table_insert (hash, NM_PPP_IP4_CONFIG_WINS, val);
231  		}
232  	
233  		g_message ("nm-ppp-plugin: (%s): sending Ip4Config to NetworkManager...", __func__);
234  	
235  		dbus_g_proxy_call_no_reply (proxy, "SetIp4Config",
236  		                            DBUS_TYPE_G_MAP_OF_VARIANT, hash, G_TYPE_INVALID,
237  		                            G_TYPE_INVALID);
238  	
239  		g_hash_table_destroy (hash);
240  	}
241  	
242  	static int
243  	get_chap_check (void)
244  	{
245  		return 1;
246  	}
247  	
248  	static int
249  	get_pap_check (void)
250  	{
251  		return 1;
252  	}
253  	
254  	static int
255  	get_credentials (char *username, char *password)
256  	{
257  		char *my_username = NULL;
258  		char *my_password = NULL;
259  		size_t len;
260  		GError *err = NULL;
261  	
(1) Event cond_false: Condition "username", taking false branch
(3) Event var_compare_op: Comparing "username" to null implies that "username" might be null.
Also see events: [var_deref_model]
262  		if (username && !password) {
263  			/* pppd is checking pap support; return 1 for supported */
264  			return 1;
(2) Event if_end: End of if statement
265  		}
266  	
(4) Event cond_true: Condition "!__inst", taking true branch
(5) Event if_fallthrough: Falling through to end of if statement
(6) Event if_end: End of if statement
(7) Event cond_true: Condition "({...})", taking true branch
(8) Event if_fallthrough: Falling through to end of if statement
(9) Event if_end: End of if statement
(10) Event cond_true: Condition "({...})", taking true branch
(11) Event if_fallthrough: Falling through to end of if statement
(12) Event if_end: End of if statement
267  		g_return_val_if_fail (DBUS_IS_G_PROXY (proxy), -1);
268  	
269  		g_message ("nm-ppp-plugin: (%s): passwd-hook, requesting credentials...", __func__);
270  	
271  		dbus_g_proxy_call (proxy, "NeedSecrets", &err,
272  		                   G_TYPE_INVALID,
273  		                   G_TYPE_STRING, &my_username,
274  		                   G_TYPE_STRING, &my_password,
275  		                   G_TYPE_INVALID);
276  	
(13) Event cond_false: Condition "err", taking false branch
277  		if (err) {
278  			g_warning ("nm-ppp-plugin: (%s): could not get secrets: (%d) %s",
279  			           __func__,
280  			           err ? err->code : -1,
281  			           err->message ? err->message : "(unknown)");
282  			g_error_free (err);
283  			return -1;
(14) Event if_end: End of if statement
284  		}
285  	
286  		g_message ("nm-ppp-plugin: (%s): got credentials from NetworkManager", __func__);
287  	
(15) Event cond_true: Condition "my_username", taking true branch
288  		if (my_username) {
289  			len = strlen (my_username) + 1;
(16) Event cond_true: Condition "len < 256", taking true branch
290  			len = len < MAXNAMELEN ? len : MAXNAMELEN;
291  	
(17) Event var_deref_model: Passing null pointer "username" to function "strncpy(char * restrict, char const * restrict, size_t)", which dereferences it.
Also see events: [var_compare_op]
292  			strncpy (username, my_username, len);
293  			username[len - 1] = '\0';
294  	
295  			g_free (my_username);
296  		}
297  	
298  		if (my_password) {
299  			len = strlen (my_password) + 1;
300  			len = len < MAXSECRETLEN ? len : MAXSECRETLEN;
301  	
302  			strncpy (password, my_password, len);
303  			password[len - 1] = '\0';
304  	
305  			g_free (my_password);
306  		}
307  	
308  		return 1;
309  	}
310  	
311  	static void
312  	nm_exit_notify (void *data, int arg)
313  	{
314  		g_return_if_fail (DBUS_IS_G_PROXY (proxy));
315  	
316  		g_message ("nm-ppp-plugin: (%s): cleaning up", __func__);
317  	
318  		g_object_unref (proxy);
319  		proxy = NULL;
320  	}
321  	
322  	int
323  	plugin_init (void)
324  	{
325  		DBusGConnection *bus;
326  		GError *err = NULL;
327  	
328  		g_type_init ();
329  	
330  		g_message ("nm-ppp-plugin: (%s): initializing", __func__);
331  	
332  		bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err);
333  		if (!bus) {
334  			g_warning ("nm-pppd-plugin: (%s): couldn't connect to system bus: (%d) %s",
335  			           __func__,
336  			           err ? err->code : -1,
337  			           err && err->message ? err->message : "(unknown)");
338  			g_error_free (err);
339  			return -1;
340  		}
341  	
342  		/* NM passes in the object path of the corresponding PPPManager
343  		 * object as the 'ipparam' argument to pppd.
344  		 */
345  		proxy = dbus_g_proxy_new_for_name (bus, NM_DBUS_SERVICE, ipparam, NM_DBUS_INTERFACE_PPP);
346  	
347  		dbus_g_connection_unref (bus);
348  	
349  		chap_passwd_hook = get_credentials;
350  		chap_check_hook = get_chap_check;
351  		pap_passwd_hook = get_credentials;
352  		pap_check_hook = get_pap_check;
353  	
354  		add_notifier (&phasechange, nm_phasechange, NULL);
355  		add_notifier (&ip_up_notifier, nm_ip_up, NULL);
356  		add_notifier (&exitnotify, nm_exit_notify, proxy);
357  	
358  		return 0;
359  	}
360