1(* OpenVPN module for Augeas 2 Author: Raphael Pinson <raphink@gmail.com> 3 Author: Justin Akers <dafugg@gmail.com> 4 5 Reference: http://openvpn.net/index.php/documentation/howto.html 6 Reference: https://community.openvpn.net/openvpn/wiki/Openvpn23ManPage 7 8 TODO: Inline file support 9*) 10 11 12module OpenVPN = 13 autoload xfm 14 15(************************************************************************ 16 * USEFUL PRIMITIVES 17 *************************************************************************) 18 19let eol = Util.eol 20let indent = Util.indent 21 22(* Define separators *) 23let sep = Util.del_ws_spc 24 25(* Define value regexps. 26 Custom simplified ipv6 used instead of Rx.ipv6 as the augeas Travis instances 27 are limited to 2GB of memory. Using 'ipv6_re = Rx.ipv6' consumes an extra 28 2GB of memory and thus the test is OOM-killed. 29*) 30let ipv6_re = /[0-9A-Fa-f:]+/ 31let ipv4_re = Rx.ipv4 32let ip_re = ipv4_re|ipv6_re 33let num_re = Rx.integer 34let fn_re = /[^#; \t\n][^#;\n]*[^#; \t\n]|[^#; \t\n]/ 35let fn_safe_re = /[^#; \t\r\n]+/ 36let an_re = /[a-z][a-z0-9_-]*/ 37let hn_re = Rx.hostname 38let port_re = /[0-9]+/ 39let host_re = ip_re|hn_re 40let proto_re = /(tcp|udp)/ 41let proto_ext_re = /(udp|tcp-client|tcp-server)/ 42let alg_re = /(none|[A-Za-z][A-Za-z0-9-]+)/ 43let ipv6_bits_re = ipv6_re . /\/[0-9]+/ 44 45(* Define store aliases *) 46let ip = store ip_re 47let num = store num_re 48let filename = store fn_re 49let filename_safe = store fn_safe_re 50let hostname = store hn_re 51let sto_to_dquote = store /[^"\n]+/ (* " Emacs, relax *) 52let port = store port_re 53let host = store host_re 54let proto = store proto_re 55let proto_ext = store proto_ext_re 56 57(* define comments and empty lines *) 58let comment = Util.comment_generic /[ \t]*[;#][ \t]*/ "# " 59let comment_or_eol = eol | Util.comment_generic /[ \t]*[;#][ \t]*/ " # " 60 61let empty = Util.empty 62 63 64(************************************************************************ 65 * SINGLE VALUES 66 * 67 * - local => IP|hostname 68 * - port => num 69 * - proto => udp|tcp-client|tcp-server 70 * - proto-force => udp|tcp 71 * - mode => p2p|server 72 * - dev => (tun|tap)\d* 73 * - dev-node => filename 74 * - ca => filename 75 * - config => filename 76 * - cert => filename 77 * - key => filename 78 * - dh => filename 79 * - ifconfig-pool-persist => filename 80 * - learn-address => filename 81 * - cipher => [A-Z0-9-]+ 82 * - max-clients => num 83 * - user => alphanum 84 * - group => alphanum 85 * - status => filename 86 * - log => filename 87 * - log-append => filename 88 * - client-config-dir => filename 89 * - verb => num 90 * - mute => num 91 * - fragment => num 92 * - mssfix => num 93 * - connect-retry num 94 * - connect-retry-max num 95 * - connect-timeout num 96 * - http-proxy-timeout num 97 * - max-routes num 98 * - ns-cert-type => "server" 99 * - resolv-retry => "infinite" 100 * - script-security => [0-3] (execve|system)? 101 * - ipchange => command 102 * - topology => type 103 *************************************************************************) 104 105let single_host = "local" | "tls-remote" 106let single_ip = "lladdr" 107let single_ipv6_bits = "iroute-ipv6" 108 | "server-ipv6" 109 | "ifconfig-ipv6-pool" 110let single_num = "port" 111 | "max-clients" 112 | "verb" 113 | "mute" 114 | "fragment" 115 | "mssfix" 116 | "connect-retry" 117 | "connect-retry-max" 118 | "connect-timeout" 119 | "http-proxy-timeout" 120 | "resolv-retry" 121 | "lport" 122 | "rport" 123 | "max-routes" 124 | "max-routes-per-client" 125 | "route-metric" 126 | "tun-mtu" 127 | "tun-mtu-extra" 128 | "shaper" 129 | "ping" 130 | "ping-exit" 131 | "ping-restart" 132 | "sndbuf" 133 | "rcvbuf" 134 | "txqueuelen" 135 | "link-mtu" 136 | "nice" 137 | "management-log-cache" 138 | "bcast-buffers" 139 | "tcp-queue-limit" 140 | "server-poll-timeout" 141 | "keysize" 142 | "pkcs11-pin-cache" 143 | "tls-timeout" 144 | "reneg-bytes" 145 | "reneg-pkts" 146 | "reneg-sec" 147 | "hand-window" 148 | "tran-window" 149let single_fn = "ca" 150 | "cert" 151 | "extra-certs" 152 | "config" 153 | "key" 154 | "dh" 155 | "log" 156 | "log-append" 157 | "client-config-dir" 158 | "dev-node" 159 | "cd" 160 | "chroot" 161 | "writepid" 162 | "client-config-dir" 163 | "tmp-dir" 164 | "replay-persist" 165 | "ca" 166 | "capath" 167 | "pkcs12" 168 | "pkcs11-id" 169 | "askpass" 170 | "tls-export-cert" 171 | "x509-track" 172let single_an = "user" 173 | "group" 174 | "management-client-user" 175 | "management-client-group" 176let single_cmd = "ipchange" 177 | "iproute" 178 | "route-up" 179 | "route-pre-down" 180 | "mark" 181 | "up" 182 | "down" 183 | "setcon" 184 | "echo" 185 | "client-connect" 186 | "client-disconnect" 187 | "learn-address" 188 | "tls-verify" 189 190let single_entry (kw:regexp) (re:regexp) 191 = [ key kw . sep . store re . comment_or_eol ] 192 193let single_opt_entry (kw:regexp) (re:regexp) 194 = [ key kw . (sep . store re)? .comment_or_eol ] 195 196let single = single_entry single_num num_re 197 | single_entry single_fn fn_re 198 | single_entry single_an an_re 199 | single_entry single_host host_re 200 | single_entry single_ip ip_re 201 | single_entry single_ipv6_bits ipv6_bits_re 202 | single_entry single_cmd fn_re 203 | single_entry "proto" proto_ext_re 204 | single_entry "proto-force" proto_re 205 | single_entry "mode" /(p2p|server)/ 206 | single_entry "dev" /(tun|tap)[0-9]*|null/ 207 | single_entry "dev-type" /(tun|tap)/ 208 | single_entry "topology" /(net30|p2p|subnet)/ 209 | single_entry "cipher" alg_re 210 | single_entry "auth" alg_re 211 | single_entry "resolv-retry" "infinite" 212 | single_entry "script-security" /[0-3]( execve| system)?/ 213 | single_entry "route-gateway" (host_re|/dhcp/) 214 | single_entry "mtu-disc" /(no|maybe|yes)/ 215 | single_entry "remap-usr1" /SIG(HUP|TERM)/ 216 | single_entry "socket-flags" /(TCP_NODELAY)/ 217 | single_entry "auth-retry" /(none|nointeract|interact)/ 218 | single_entry "tls-version-max" Rx.decimal 219 | single_entry "verify-hash" /([A-Za-z0-9]{2}:)+[A-Za-z0-9]{2}/ 220 | single_entry "pkcs11-cert-private" /[01]/ 221 | single_entry "pkcs11-protected-authentication" /[01]/ 222 | single_entry "pkcs11-private-mode" /[A-Za-z0-9]+/ 223 | single_entry "key-method" /[12]/ 224 | single_entry "ns-cert-type" /(client|server)/ 225 | single_entry "remote-cert-tls" /(client|server)/ 226 227let single_opt = single_opt_entry "comp-lzo" /(yes|no|adaptive)/ 228 | single_opt_entry "syslog" fn_re 229 | single_opt_entry "daemon" fn_re 230 | single_opt_entry "auth-user-pass" fn_re 231 | single_opt_entry "explicit-exit-notify" num_re 232 | single_opt_entry "engine" fn_re 233 234(************************************************************************ 235 * DOUBLE VALUES 236 *************************************************************************) 237 238let double_entry (kw:regexp) (a:string) (aval:regexp) (b:string) (bval:regexp) 239 = [ key kw 240 . sep . [ label a . store aval ] 241 . sep . [ label b . store bval ] 242 . comment_or_eol 243 ] 244 245let double_secopt_entry (kw:regexp) (a:string) (aval:regexp) (b:string) (bval:regexp) 246 = [ key kw 247 . sep . [ label a . store aval ] 248 . (sep . [ label b . store bval ])? 249 . comment_or_eol 250 ] 251 252 253let double = double_entry "keepalive" "ping" num_re "timeout" num_re 254 | double_entry "hash-size" "real" num_re "virtual" num_re 255 | double_entry "ifconfig" "local" ip_re "remote" ip_re 256 | double_entry "connect-freq" "num" num_re "sec" num_re 257 | double_entry "verify-x509-name" "name" hn_re "type" 258 /(subject|name|name-prefix)/ 259 | double_entry "ifconfig-ipv6" "address" ipv6_bits_re "remote" ipv6_re 260 | double_entry "ifconfig-ipv6-push" "address" ipv6_bits_re "remote" ipv6_re 261 | double_secopt_entry "iroute" "local" ip_re "netmask" ip_re 262 | double_secopt_entry "stale-routes-check" "age" num_re "interval" num_re 263 | double_secopt_entry "ifconfig-pool-persist" 264 "file" fn_safe_re "seconds" num_re 265 | double_secopt_entry "secret" "file" fn_safe_re "direction" /[01]/ 266 | double_secopt_entry "prng" "algorithm" alg_re "nsl" num_re 267 | double_secopt_entry "replay-window" "window-size" num_re "seconds" num_re 268 269 270(************************************************************************ 271 * FLAGS 272 *************************************************************************) 273 274let flag_words = "client-to-client" 275 | "duplicate-cn" 276 | "persist-key" 277 | "persist-tun" 278 | "client" 279 | "remote-random" 280 | "nobind" 281 | "mute-replay-warnings" 282 | "http-proxy-retry" 283 | "socks-proxy-retry" 284 | "remote-random-hostname" 285 | "show-proxy-settings" 286 | "float" 287 | "bind" 288 | "nobind" 289 | "tun-ipv6" 290 | "ifconfig-noexec" 291 | "ifconfig-nowarn" 292 | "route-noexec" 293 | "route-nopull" 294 | "allow-pull-fqdn" 295 | "mtu-test" 296 | "ping-timer-rem" 297 | "persist-tun" 298 | "persist-local-ip" 299 | "persist-remote-ip" 300 | "mlock" 301 | "up-delay" 302 | "down-pre" 303 | "up-restart" 304 | "disable-occ" 305 | "errors-to-stderr" 306 | "passtos" 307 | "suppress-timestamps" 308 | "fast-io" 309 | "multihome" 310 | "comp-noadapt" 311 | "management-client" 312 | "management-query-passwords" 313 | "management-query-proxy" 314 | "management-query-remote" 315 | "management-forget-disconnect" 316 | "management-hold" 317 | "management-signal" 318 | "management-up-down" 319 | "management-client-auth" 320 | "management-client-pf" 321 | "push-reset" 322 | "push-peer-info" 323 | "disable" 324 | "ifconfig-pool-linear" 325 | "client-to-client" 326 | "duplicate-cn" 327 | "ccd-exclusive" 328 | "tcp-nodelay" 329 | "opt-verify" 330 | "auth-user-pass-optional" 331 | "client-cert-not-required" 332 | "username-as-common-name" 333 | "pull" 334 | "key-direction" 335 | "no-replay" 336 | "mute-replay-warnings" 337 | "no-iv" 338 | "use-prediction-resistance" 339 | "test-crypto" 340 | "tls-server" 341 | "tls-client" 342 | "pkcs11-id-management" 343 | "single-session" 344 | "tls-exit" 345 | "auth-nocache" 346 | "show-ciphers" 347 | "show-digests" 348 | "show-tls" 349 | "show-engines" 350 | "genkey" 351 | "mktun" 352 | "rmtun" 353 354 355let flag_entry (kw:regexp) 356 = [ key kw . comment_or_eol ] 357 358let flag = flag_entry flag_words 359 360 361(************************************************************************ 362 * OTHER FIELDS 363 * 364 * - server => IP IP [nopool] 365 * - server-bridge => IP IP IP IP 366 * - route => host host [host [num]] 367 * - push => "string" 368 * - tls-auth => filename [01] 369 * - remote => hostname/IP [num] [(tcp|udp)] 370 * - management => IP num filename 371 * - http-proxy => host port [filename|keyword] [method] 372 * - http-proxy-option => (VERSION decimal|AGENT string) 373 * ... 374 * and many others 375 * 376 *************************************************************************) 377 378let server = [ key "server" 379 . sep . [ label "address" . ip ] 380 . sep . [ label "netmask" . ip ] 381 . (sep . [ key "nopool" ]) ? 382 . comment_or_eol 383 ] 384 385let server_bridge = 386 let ip_params = [ label "address" . ip ] . sep 387 . [ label "netmask" . ip ] . sep 388 . [ label "start" . ip ] . sep 389 . [ label "end" . ip ] in 390 [ key "server-bridge" 391 . sep . (ip_params|store /(nogw)/) 392 . comment_or_eol 393 ] 394 395let route = 396 let route_net_kw = store (/(vpn_gateway|net_gateway|remote_host)/|host_re) in 397 [ key "route" . sep 398 . [ label "address" . route_net_kw ] 399 . (sep . [ label "netmask" . store (ip_re|/default/) ] 400 . (sep . [ label "gateway" . route_net_kw ] 401 . (sep . [ label "metric" . store (/default/|num_re)] )? 402 )? 403 )? 404 . comment_or_eol 405 ] 406 407let route_ipv6 = 408 let route_net_re = /(vpn_gateway|net_gateway|remote_host)/ in 409 [ key "route-ipv6" . sep 410 . [ label "network" . store (route_net_re|ipv6_bits_re) ] 411 . (sep . [ label "gateway" . store (route_net_re|ipv6_re) ] 412 . (sep . [ label "metric" . store (/default/|num_re)] )? 413 )? 414 . comment_or_eol 415 ] 416 417let push = [ key "push" . sep 418 . Quote.do_dquote sto_to_dquote 419 . comment_or_eol 420 ] 421 422let tls_auth = [ key "tls-auth" . sep 423 . [ label "key" . filename ] . sep 424 . [ label "is_client" . store /[01]/ ] . comment_or_eol 425 ] 426 427let remote = [ key "remote" . sep 428 . [ label "server" . host ] 429 . (sep . [label "port" . port] 430 . (sep . [label "proto" . proto]) ? ) ? 431 . comment_or_eol 432 ] 433 434let http_proxy = 435 let auth_method_re = /(none|basic|ntlm)/ in 436 let auth_method = store auth_method_re in 437 [ key "http-proxy" 438 . sep . [ label "server" . host ] 439 . sep . [ label "port" . port ] 440 . (sep . [ label "auth" . filename_safe ] 441 . (sep . [ label "auth-method" . auth_method ]) ? )? 442 . comment_or_eol 443 ] 444 445let http_proxy_option = [ key "http-proxy-option" 446 . sep . [ label "option" . store /(VERSION|AGENT)/ ] 447 . sep . [ label "value" . filename ] 448 . comment_or_eol 449 ] 450 451let socks_proxy = [ key "socks-proxy" 452 . sep . [ label "server" . host ] 453 . (sep . [ label "port" . port ] 454 . (sep . [ label "auth" . filename_safe ])? )? 455 . comment_or_eol 456 ] 457 458let port_share = [ key "port-share" 459 . sep . [ label "host" . host ] 460 . sep . [ label "port" . port ] 461 . (sep . [ label "dir" . filename ])? 462 . comment_or_eol 463 ] 464 465let route_delay = [ key "route-delay" 466 . (sep . [ label "seconds" . num ] 467 . (sep . [ label "win-seconds" . num ] ) ? 468 )? 469 . comment_or_eol 470 ] 471 472let inetd = [ key "inetd" 473 . (sep . [label "mode" . store /(wait|nowait)/ ] 474 . (sep . [ label "progname" . filename ] ) ? 475 )? 476 . comment_or_eol 477 ] 478 479let inactive = [ key "inactive" 480 . sep . [ label "seconds" . num ] 481 . (sep . [ label "bytes" . num ] ) ? 482 . comment_or_eol 483 ] 484 485let client_nat = [ key "client-nat" 486 . sep . [ label "type" . store /(snat|dnat)/ ] 487 . sep . [ label "network" . ip ] 488 . sep . [ label "netmask" . ip ] 489 . sep . [ label "alias" . ip ] 490 . comment_or_eol 491 ] 492 493let status = [ key "status" 494 . sep . [ label "file" . filename_safe ] 495 . (sep . [ label "repeat-seconds" . num ]) ? 496 . comment_or_eol 497 ] 498 499let plugin = [ key "plugin" 500 . sep . [ label "file" . filename_safe ] 501 . (sep . [ label "init-string" . filename ]) ? 502 . comment_or_eol 503 ] 504 505let management = [ key "management" . sep 506 . [ label "server" . ip ] 507 . sep . [ label "port" . port ] 508 . (sep . [ label "pwfile" . filename ] ) ? 509 . comment_or_eol 510 ] 511 512let auth_user_pass_verify = [ key "auth-user-pass-verify" 513 . sep . [ Quote.quote_spaces (label "command") ] 514 . sep . [ label "method" . store /via-(env|file)/ ] 515 . comment_or_eol 516 ] 517 518let static_challenge = [ key "static-challenge" 519 . sep . [ Quote.quote_spaces (label "text") ] 520 . sep . [ label "echo" . store /[01]/ ] 521 . comment_or_eol 522 ] 523 524let cryptoapicert = [ key "cryptoapicert" . sep . Quote.dquote 525 . [ key /[A-Z]+/ . Sep.colon . store /[A-Za-z _-]+/ ] 526 . Quote.dquote . comment_or_eol 527 ] 528 529let setenv = 530 let envvar = /[^#;\/ \t\n][A-Za-z0-9_-]+/ in 531 [ key ("setenv"|"setenv-safe") 532 . sep . [ key envvar . sep . store fn_re ] 533 . comment_or_eol 534 ] 535 536let redirect = 537 let redirect_flag = /(local|autolocal|def1|bypass-dhcp|bypass-dns|block-local)/ in 538 let redirect_key = "redirect-gateway" | "redirect-private" in 539 [ key redirect_key 540 . (sep . [ label "flag" . store redirect_flag ] ) + 541 . comment_or_eol 542 ] 543 544let tls_cipher = 545 let ciphername = /[A-Za-z0-9!_-]+/ in 546 [ key "tls-cipher" . sep 547 . [label "cipher" . store ciphername] 548 . (Sep.colon . [label "cipher" . store ciphername])* 549 . comment_or_eol 550 ] 551 552let remote_cert_ku = 553 let usage = [label "usage" . store /[A-Za-z0-9]{1,2}/] in 554 [ key "remote-cert-ku" . sep . usage . (sep . usage)* . comment_or_eol ] 555 556(* FIXME: Surely there's a nicer way to do this *) 557let remote_cert_eku = 558 let oid = [label "oid" . store /[0-9]+\.([0-9]+\.)*[0-9]+/] in 559 let symbolic = [Quote.do_quote_opt 560 (label "symbol" . store /[A-Za-z0-9][A-Za-z0-9 _-]*[A-Za-z0-9]/)] in 561 [ key "remote-cert-eku" . sep . (oid|symbolic) . comment_or_eol ] 562 563let status_version = [ key "status-version" 564 . (sep . num) ? 565 . comment_or_eol 566 ] 567 568let ifconfig_pool = [ key "ifconfig-pool" 569 . sep . [ label "start" . ip ] 570 . sep . [ label "end" . ip ] 571 . (sep . [ label "netmask" . ip ])? 572 . comment_or_eol 573 ] 574 575let ifconfig_push = [ key "ifconfig-push" 576 . sep . [ label "local" . ip ] 577 . sep . [ label "remote-netmask" . ip ] 578 . (sep . [ label "alias" . store /[A-Za-z0-9_-]+/ ] )? 579 . comment_or_eol 580 ] 581 582let ignore_unknown_option = [ key "ignore-unknown-option" 583 . (sep . [ label "opt" . store /[A-Za-z0-9_-]+/ ] ) + 584 . comment_or_eol 585 ] 586 587let tls_version_min = [ key "tls-version-min" 588 . sep . store Rx.decimal 589 . (sep . [ key "or-highest" ]) ? 590 . comment_or_eol 591 ] 592 593let crl_verify = [ key "crl-verify" 594 . sep . filename_safe 595 . (sep . [ key "dir" ]) ? 596 . comment_or_eol 597 ] 598 599let x509_username_field = 600 let fieldname = /[A-Za-z0-9_-]+/ in 601 let extfield = ([key /ext/ . Sep.colon . store fieldname]) in 602 let subjfield = ([label "subj" . store fieldname]) in 603 [ key "x509-username-field" 604 . sep . (extfield|subjfield) 605 . comment_or_eol 606 ] 607 608let other = server 609 | server_bridge 610 | route 611 | push 612 | tls_auth 613 | remote 614 | http_proxy 615 | http_proxy_option 616 | socks_proxy 617 | management 618 | route_delay 619 | client_nat 620 | redirect 621 | inactive 622 | setenv 623 | inetd 624 | status 625 | status_version 626 | plugin 627 | ifconfig_pool 628 | ifconfig_push 629 | ignore_unknown_option 630 | auth_user_pass_verify 631 | port_share 632 | static_challenge 633 | tls_version_min 634 | tls_cipher 635 | cryptoapicert 636 | x509_username_field 637 | remote_cert_ku 638 | remote_cert_eku 639 | crl_verify 640 | route_ipv6 641 642 643(************************************************************************ 644 * LENS & FILTER 645 *************************************************************************) 646 647let lns = ( comment | empty | single | single_opt | double | flag | other )* 648 649let filter = (incl "/etc/openvpn/client.conf") 650 . (incl "/etc/openvpn/server.conf") 651 652let xfm = transform lns filter 653 654 655 656