/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to plugin-helpers/mandos-client-iprouteadddel.c

  • Committer: Teddy Hogeborn
  • Date: 2021-02-03 23:10:42 UTC
  • Revision ID: teddy@recompile.se-20210203231042-2z3egrvpo1zt7nej
mandos-ctl: Fix bad test for command.Remove and related minor issues

The test for command.Remove removes all clients from the spy server,
and then loops over all clients, looking for the corresponding Remove
command as recorded by the spy server.  But since since there aren't
any clients left after they were removed, no assertions are made, and
the test therefore does nothing.  Fix this.

In tests for command.Approve and command.Deny, add checks that clients
were not somehow removed by the command (in which case, likewise, no
assertions are made).

Add related checks to TestPropertySetterCmd.runTest; i.e. test that a
sequence is not empty before looping over it and making assertions.

* mandos-ctl (TestBaseCommands.test_Remove): Save a copy of the
  original "clients" dict, and loop over those instead.  Add assertion
  that all clients were indeed removed.  Also fix the code which looks
  for the Remove command, which now needs to actually work.
  (TestBaseCommands.test_Approve, TestBaseCommands.test_Deny): Add
  assertion that there are still clients before looping over them.
  (TestPropertySetterCmd.runTest): Add assertion that the list of
  values to get is not empty before looping over them.  Also add check
  that there are still clients before looping over clients.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*  -*- coding: utf-8 -*- */
 
2
/* 
 
3
 * iprouteadddel - Add or delete direct route to a local IP address
 
4
 * 
 
5
 * Copyright © 2015-2018, 2021 Teddy Hogeborn
 
6
 * Copyright © 2015-2018, 2021 Björn Påhlsson
 
7
 * 
 
8
 * This file is part of Mandos.
 
9
 * 
 
10
 * Mandos is free software: you can redistribute it and/or modify it
 
11
 * under the terms of the GNU General Public License as published by
 
12
 * the Free Software Foundation, either version 3 of the License, or
 
13
 * (at your option) any later version.
 
14
 * 
 
15
 * Mandos is distributed in the hope that it will be useful, but
 
16
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
18
 * General Public License for more details.
 
19
 * 
 
20
 * You should have received a copy of the GNU General Public License
 
21
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
 
22
 * 
 
23
 * Contact the authors at <mandos@recompile.se>.
 
24
 */
 
25
 
 
26
#define _GNU_SOURCE             /* program_invocation_short_name */
 
27
#include <stdbool.h>            /* bool, false, true */
 
28
#include <argp.h>               /* argp_program_version,
 
29
                                   argp_program_bug_address,
 
30
                                   struct argp_option,
 
31
                                   struct argp_state, ARGP_KEY_ARG,
 
32
                                   argp_usage(), ARGP_KEY_END,
 
33
                                   ARGP_ERR_UNKNOWN, struct argp,
 
34
                                   argp_parse(), ARGP_IN_ORDER */
 
35
#include <errno.h>              /* errno,
 
36
                                   program_invocation_short_name,
 
37
                                   error_t, EINVAL, ENOMEM */
 
38
#include <stdio.h>              /* fprintf(), stderr, perror(), FILE,
 
39
                                   vfprintf() */
 
40
#include <stdarg.h>             /* va_list, va_start(), vfprintf() */
 
41
#include <stdlib.h>             /* EXIT_SUCCESS */
 
42
#include <netlink/netlink.h>    /* struct nl_addr, nl_addr_parse(),
 
43
                                   nl_geterror(),
 
44
                                   nl_addr_get_family(), NLM_F_EXCL,
 
45
                                   nl_addr_put() */
 
46
#include <stddef.h>             /* NULL */
 
47
#include <netlink/route/route.h>/* struct rtnl_route,
 
48
                                   struct rtnl_nexthop, NETLINK_ROUTE,
 
49
                                   rtnl_route_alloc(),
 
50
                                   rtnl_route_set_family(),
 
51
                                   rtnl_route_set_protocol(),
 
52
                                   RTPROT_BOOT,
 
53
                                   rtnl_route_set_scope(),
 
54
                                   RT_SCOPE_LINK,
 
55
                                   rtnl_route_set_type(), RTN_UNICAST,
 
56
                                   rtnl_route_set_dst(),
 
57
                                   rtnl_route_set_table(),
 
58
                                   RT_TABLE_MAIN,
 
59
                                   rtnl_route_nh_alloc(),
 
60
                                   rtnl_route_nh_set_ifindex(),
 
61
                                   rtnl_route_add_nexthop(),
 
62
                                   rtnl_route_add(),
 
63
                                   rtnl_route_delete(),
 
64
                                   rtnl_route_put(),
 
65
                                   rtnl_route_nh_free() */
 
66
#include <netlink/socket.h>     /* struct nl_sock, nl_socket_alloc(),
 
67
                                   nl_connect(), nl_socket_free() */
 
68
#include <strings.h>            /* strcasecmp() */
 
69
#include <sys/socket.h>         /* AF_UNSPEC, AF_INET6, AF_INET */
 
70
#include <sysexits.h>           /* EX_USAGE, EX_OSERR */
 
71
#include <netlink/route/link.h> /* struct rtnl_link,
 
72
                                   rtnl_link_get_kernel(),
 
73
                                   rtnl_link_get_ifindex(),
 
74
                                   rtnl_link_put() */
 
75
#include <netinet/in.h>         /* sa_family_t */
 
76
#include <inttypes.h>           /* PRIdMAX, intmax_t */
 
77
#include <stdint.h>             /* uint8_t */
 
78
 
 
79
 
 
80
bool debug = false;
 
81
const char *argp_program_version = "mandos-client-iprouteadddel " VERSION;
 
82
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
83
 
 
84
/* Function to use when printing errors */
 
85
void perror_plus(const char *print_text){
 
86
  int e = errno;
 
87
  fprintf(stderr, "Mandos plugin helper %s: ",
 
88
          program_invocation_short_name);
 
89
  errno = e;
 
90
  perror(print_text);
 
91
}
 
92
 
 
93
__attribute__((format (gnu_printf, 2, 3), nonnull))
 
94
int fprintf_plus(FILE *stream, const char *format, ...){
 
95
  va_list ap;
 
96
  va_start(ap, format);
 
97
  
 
98
  fprintf(stream, "Mandos plugin helper %s: ",
 
99
          program_invocation_short_name);
 
100
  return vfprintf(stream, format, ap);
 
101
}
 
102
 
 
103
int main(int argc, char *argv[]){
 
104
  int ret;
 
105
  int exitcode = EXIT_SUCCESS;
 
106
  struct arguments {
 
107
    bool add;                   /* true: add, false: delete */
 
108
    char *address;              /* IP address as string */
 
109
    struct nl_addr *nl_addr;    /* Netlink IP address */
 
110
    char *interface;            /* interface name */
 
111
  } arguments = { .add = true, .address = NULL, .interface = NULL };
 
112
  struct argp_option options[] = {
 
113
    { .name = "debug", .key = 128,
 
114
      .doc = "Debug mode" },
 
115
    { .name = NULL }
 
116
  };
 
117
  struct rtnl_route *route = NULL;
 
118
  struct rtnl_nexthop *nexthop = NULL;
 
119
  struct nl_sock *sk = NULL;
 
120
  
 
121
  error_t parse_opt(int key, char *arg, struct argp_state *state){
 
122
    int lret;
 
123
    errno = 0;
 
124
    switch(key){
 
125
    case 128:                   /* --debug */
 
126
      debug = true;
 
127
      break;
 
128
    case ARGP_KEY_ARG:
 
129
      switch(state->arg_num){
 
130
      case 0:
 
131
        if(strcasecmp(arg, "add") == 0){
 
132
          ((struct arguments *)(state->input))->add = true;
 
133
        } else if(strcasecmp(arg, "delete") == 0){
 
134
          ((struct arguments *)(state->input))->add = false;
 
135
        } else {
 
136
          fprintf_plus(stderr, "Unrecognized command: %s\n", arg);
 
137
          argp_usage(state);
 
138
        }
 
139
        break;
 
140
      case 1:
 
141
        ((struct arguments *)(state->input))->address = arg;
 
142
        lret = nl_addr_parse(arg, AF_UNSPEC, &(((struct arguments *)
 
143
                                                (state->input))
 
144
                                               ->nl_addr));
 
145
        if(lret != 0){
 
146
          fprintf_plus(stderr, "Failed to parse address %s: %s\n",
 
147
                       arg, nl_geterror(lret));
 
148
          argp_usage(state);
 
149
        }
 
150
        break;
 
151
      case 2:
 
152
        ((struct arguments *)(state->input))->interface = arg;
 
153
        break;
 
154
      default:
 
155
        argp_usage(state);
 
156
      }
 
157
      break;
 
158
    case ARGP_KEY_END:
 
159
      if(state->arg_num < 3){
 
160
        argp_usage(state);
 
161
      }
 
162
      break;
 
163
    default:
 
164
      return ARGP_ERR_UNKNOWN;
 
165
    }
 
166
    return errno;
 
167
  }
 
168
  
 
169
  struct argp argp = { .options = options, .parser = parse_opt,
 
170
                       .args_doc = "[ add | delete ] ADDRESS INTERFACE",
 
171
                       .doc = "Mandos client helper -- Add or delete"
 
172
                       " local route to IP address on interface" };
 
173
  
 
174
  ret = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &arguments);
 
175
  switch(ret){
 
176
  case 0:
 
177
    break;
 
178
  case EINVAL:
 
179
    exit(EX_USAGE);
 
180
  case ENOMEM:
 
181
  default:
 
182
    errno = ret;
 
183
    perror_plus("argp_parse");
 
184
    exitcode = EX_OSERR;
 
185
    goto end;
 
186
  }
 
187
  /* Get netlink socket */
 
188
  sk = nl_socket_alloc();
 
189
  if(sk == NULL){
 
190
    fprintf_plus(stderr, "Failed to allocate netlink socket: %s\n",
 
191
                 nl_geterror(ret));
 
192
    exitcode = EX_OSERR;
 
193
    goto end;
 
194
  }
 
195
  /* Connect socket to netlink */
 
196
  ret = nl_connect(sk, NETLINK_ROUTE);
 
197
  if(ret < 0){
 
198
    fprintf_plus(stderr, "Failed to connect socket to netlink: %s\n",
 
199
                 nl_geterror(ret));
 
200
    exitcode = EX_OSERR;
 
201
    goto end;
 
202
  }
 
203
  /* Get link object of specified interface */
 
204
  struct rtnl_link *link = NULL;
 
205
  ret = rtnl_link_get_kernel(sk, 0, arguments.interface, &link);
 
206
  if(ret < 0){
 
207
    fprintf_plus(stderr, "Failed to use interface %s: %s\n",
 
208
                 arguments.interface, nl_geterror(ret));
 
209
    exitcode = EX_OSERR;
 
210
    goto end;
 
211
  }
 
212
  /* Get netlink route object */
 
213
  route = rtnl_route_alloc();
 
214
  if(route == NULL){
 
215
    fprintf_plus(stderr, "Failed to get netlink route:\n");
 
216
    exitcode = EX_OSERR;
 
217
    goto end;
 
218
  }
 
219
  /* Get address family of specified address */
 
220
  sa_family_t af = (sa_family_t)nl_addr_get_family(arguments.nl_addr);
 
221
  if(debug){
 
222
    fprintf_plus(stderr, "Address family of %s is %s (%" PRIdMAX
 
223
                 ")\n", arguments.address,
 
224
                 af == AF_INET6 ? "AF_INET6" :
 
225
                 ( af == AF_INET ? "AF_INET" : "UNKNOWN"),
 
226
                 (intmax_t)af);
 
227
  }
 
228
  /* Set route parameters: */
 
229
  rtnl_route_set_family(route, (uint8_t)af);   /* Address family */
 
230
  rtnl_route_set_protocol(route, RTPROT_BOOT); /* protocol - see
 
231
                                                  ip-route(8) */
 
232
  rtnl_route_set_scope(route, RT_SCOPE_LINK); /* link scope */
 
233
  rtnl_route_set_type(route, RTN_UNICAST);    /* normal unicast
 
234
                                                 address route */
 
235
  rtnl_route_set_dst(route, arguments.nl_addr); /* Destination
 
236
                                                   address */
 
237
  rtnl_route_set_table(route, RT_TABLE_MAIN); /* "main" routing
 
238
                                                 table */
 
239
  /* Create nexthop */
 
240
  nexthop = rtnl_route_nh_alloc();
 
241
  if(nexthop == NULL){
 
242
    fprintf_plus(stderr, "Failed to get netlink route nexthop\n");
 
243
    exitcode = EX_OSERR;
 
244
    goto end;
 
245
  }
 
246
  /* Get index number of specified interface */
 
247
  int ifindex = rtnl_link_get_ifindex(link);
 
248
  if(debug){
 
249
    fprintf_plus(stderr, "ifindex of %s is %d\n", arguments.interface,
 
250
                 ifindex);
 
251
  }
 
252
  /* Set interface index number on nexthop object */
 
253
  rtnl_route_nh_set_ifindex(nexthop, ifindex);
 
254
  /* Set route to use nexthop object */
 
255
  rtnl_route_add_nexthop(route, nexthop);
 
256
  /* Add or delete route? */
 
257
  if(arguments.add){
 
258
    ret = rtnl_route_add(sk, route, NLM_F_EXCL);
 
259
  } else {
 
260
    ret = rtnl_route_delete(sk, route, 0);
 
261
  }
 
262
  if(ret < 0){
 
263
     fprintf_plus(stderr, "Failed to %s route: %s\n",
 
264
                  arguments.add ? "add" : "delete",
 
265
                  nl_geterror(ret));
 
266
    exitcode = EX_OSERR;
 
267
    goto end;
 
268
  }
 
269
 end:
 
270
  /* Deallocate route */
 
271
  if(route){
 
272
    rtnl_route_put(route);
 
273
  } else if(nexthop) {
 
274
    /* Deallocate route nexthop */
 
275
    rtnl_route_nh_free(nexthop);
 
276
  }
 
277
  /* Deallocate parsed address */
 
278
  if(arguments.nl_addr){
 
279
    nl_addr_put(arguments.nl_addr);
 
280
  }
 
281
  /* Deallocate link struct */
 
282
  if(link){
 
283
    rtnl_link_put(link);
 
284
  }
 
285
  /* Deallocate netlink socket struct */
 
286
   if(sk){
 
287
    nl_socket_free(sk);
 
288
  }
 
289
  return exitcode;
 
290
}