ios_config 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.DS_Store ADDED
Binary file
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in apple_payloads.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2013 Taylor Boyko
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ the Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # ios_config
2
+
3
+ This gem provides an easy way to generate profiles and configuration payloads for use with Apple iOS devices. These profiles and payloads can be delivered via Apple MDM or Apple's Configurator or iPhone Configuration Utility (IPCU).
4
+
5
+ Not all of the possible configuration payloads have been implemented yet. Some options may be missing. If you need a particular payload or need additional support from an existing payload, please fork and implement it so that we can all benefit from your efforts!
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'ios_config'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install ios_config
20
+
21
+ ## Usage
22
+
23
+ Profiles contain basic identification and labeling information in addition to a collection of payloads. When you use this gem, you'll want to do two things:
24
+
25
+ 1. Create your payloads
26
+ 2. Create a profile that includes your payloads
27
+
28
+ ### Example
29
+
30
+ Here is an example of how you might do this:
31
+
32
+ ```ruby
33
+ # Create a payload
34
+
35
+ vpn_payload = IOSConfig::Payload::VPN.new connection_name: "My VPN",
36
+ authentication_type: :password,
37
+ connection_type: :pptp,
38
+ encryption_level: :auto,
39
+ proxy_type: :none,
40
+ send_all_traffic? true,
41
+ server: "example.org",
42
+ username: "macdemarco",
43
+ password: "viceroy"
44
+
45
+ payloads = [vpn_payload.build]
46
+
47
+ # Create a profile instance
48
+
49
+ profile = IOSConfig::Profile.new type: "Configuration",
50
+ display_name: "A Profile Name",
51
+ identifier: "org.example.examplemdmservice.exampleprofile",
52
+ organization: "A Company Name",
53
+ uuid: SecureRandom.uuid,
54
+ allow_removal: false,
55
+ client_certs: [OpenSSL::X509::Certificate.new], # Array of client certificates
56
+ payloads: payloads # must be an array when type is "Configuration"
57
+
58
+
59
+ # Generate a plist version of the profile, ready for delivery to the device
60
+
61
+ unsigned_profile = profile.unsigned
62
+
63
+ # Or, generate a signed plist version of the profile
64
+
65
+ signed_profile = profile.signed( signing_cert_path,
66
+ signing_cert_intermediate_path
67
+ signing_cert_key_path )
68
+ ```
69
+
70
+ ### Profile
71
+
72
+ ```ruby
73
+ # Create the profile
74
+
75
+ profile = IOSConfig::Profile.new [parameters]
76
+
77
+ # Then, generate your config one of two ways:
78
+
79
+ profile_plist = profile.unsigned # OR
80
+ profile_plist = profile.signed( [signing_cert_path], [intermediate_path], [key_path] )
81
+ ```
82
+
83
+ #### Parameters
84
+
85
+ ```ruby
86
+ allow_removal # if profile can be deleted by device user. defaults to true
87
+ description # (optional) displayed in device settings
88
+ display_name # displayed in device settings
89
+ identifier
90
+ organization # (optional) displayed in device settings
91
+ type # (optional) default is 'Configuration'
92
+ uuid
93
+ version # (optional) defaults to '1'
94
+ payloads # (optional) payloads to be contained in the profile. should be an array if type is 'Configuration'
95
+ client_certs # (optional) certificates used to encypt payloads
96
+ ```
97
+
98
+ ### Payloads
99
+
100
+ #### Common Parameters
101
+
102
+ ```ruby
103
+ uuid
104
+ identifier
105
+ description
106
+ ```
107
+
108
+ #### VPN
109
+
110
+ ```ruby
111
+ payload = IOSConfig::Payload::VPN.new(parameters).build
112
+ ```
113
+
114
+ Available parameters:
115
+
116
+ ```ruby
117
+ connection_name
118
+ authentication_type # :password, :rsa_securid
119
+ connection_type # :l2tp, :pptp, :ipsec, :anyconnect, :juniper_ssl, :f5_ssl, :sonicwall_modile_connect, :aruba_via
120
+ encryption_level # :none, :manual, :auto
121
+ group_name
122
+ prompt_for_password # true, false
123
+ proxy_type # :none, :manual, :auto
124
+ proxy_port
125
+ proxy_server
126
+ send_all_traffic # true, false
127
+ server
128
+ proxy_url
129
+ group_or_domain
130
+ password
131
+ username
132
+ proxy_password
133
+ proxy_username
134
+ realm
135
+ role
136
+ shared_secret
137
+ ```
138
+
139
+ ## Contributing
140
+
141
+ 1. Fork it
142
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
143
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
144
+ 4. Push to the branch (`git push origin my-new-feature`)
145
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ios_config/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ios_config"
8
+ spec.version = IOSConfig::VERSION
9
+ spec.authors = ["Taylor Boyko"]
10
+ spec.email = ["taylorboyko@gmail.com"]
11
+ spec.description = %q{Generate configuration profiles and payloads for Apple iOS devices}
12
+ spec.summary = %q{This gen provides an easy way to generate profiles and configuration payloads for use with Apple iOS devices. These profiles and payloads can be delivered via Apple MDM or Apple's Configurator or iPhone Configuration Utility (IPCU).}
13
+ spec.homepage = "https://github.com/tboyko/ios_config"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "CFPropertyList", "~> 2.2"
22
+ spec.add_development_dependency "bundler", "~> 1.3"
23
+ spec.add_development_dependency "rake"
24
+ end
data/lib/.DS_Store ADDED
Binary file
@@ -0,0 +1,7 @@
1
+ unless String.new.respond_to? 'blank?'
2
+ class String
3
+ def blank?
4
+ self.nil? || self.empty?
5
+ end
6
+ end
7
+ end
Binary file
@@ -0,0 +1,42 @@
1
+ require 'securerandom'
2
+
3
+ module IOSConfig
4
+ module Payload
5
+ class Base
6
+
7
+ attr_accessor :uuid, :identifier, :description
8
+
9
+ def initialize(attributes = {})
10
+ attributes ||= {}
11
+ attributes.each do |name, value|
12
+ begin
13
+ send("#{name}=", value)
14
+ rescue NoMethodError => e
15
+ raise ArgumentError, %{"#{name}" is not a valid attribute}
16
+ end
17
+ end
18
+
19
+ @uuid ||= SecureRandom.uuid
20
+ @identifier ||= @uuid.downcase.delete("^a-z0-9\.")
21
+ @description ||= ""
22
+ end
23
+
24
+ def build
25
+ p = { 'PayloadType' => payload_type,
26
+ 'PayloadVersion' => 1,
27
+ 'PayloadUUID' => @uuid,
28
+ 'PayloadIdentifier' => @identifier,
29
+ 'PayloadDescription' => @description }
30
+
31
+ p.merge payload
32
+ end
33
+
34
+ private
35
+
36
+ def payload_type
37
+ raise NotImplementedError
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,155 @@
1
+ module IOSConfig
2
+ module Payload
3
+ class VPN < Base
4
+
5
+ attr_accessor :authentication_type, # :password, :rsa_securid
6
+ :connection_type, # :l2tp, :pptp, :ipsec, :anyconnect, :juniper_ssl, :f5_ssl, :sonicwall_modile_connect, :aruba_via
7
+ :encryption_level, # :none, :manual, :auto
8
+ :group_name,
9
+ :connection_name,
10
+ :prompt_for_password,
11
+ :proxy_type, # :none, :manual, :auto
12
+ :proxy_port,
13
+ :proxy_server,
14
+ :send_all_traffic,
15
+ :server,
16
+ :proxy_url,
17
+ :group_or_domain,
18
+ :password,
19
+ :username,
20
+ :proxy_password,
21
+ :proxy_username,
22
+ :realm,
23
+ :role,
24
+ :shared_secret
25
+
26
+ private
27
+
28
+ def payload_type
29
+ "com.apple.vpn.managed"
30
+ end
31
+
32
+ def payload
33
+ p = { 'UserDefinedName' => @connection_name,
34
+ 'IPv4' => { 'OverridePrimary' => 1 } }
35
+
36
+ # Connection Type Specifics
37
+
38
+ case @connection_type
39
+ when :l2tp
40
+ p['VPNType'] = 'L2TP'
41
+ p_ipsec = { 'AuthenticationMethod' => 'SharedSecret',
42
+ 'LocalIdentifierType' => 'KeyID' }
43
+ p_ipsec['SharedSecret'] = StringIO.new(@shared_secret) unless @shared_secret.blank?
44
+ p['IPSec'] = p_ipsec
45
+ p['PPP'] = generate_ppp_config
46
+
47
+ when :pptp
48
+ p['VPNType'] = 'PPTP'
49
+ p['PPP'] = generate_ppp_config.merge({ 'CPPEnabled' => @encryption_level != :none,
50
+ 'CCPMPPE128Enabled' => @encryption_level != :none,
51
+ 'CCPMPPE40Enabled' => @encryption_level == :auto,
52
+ 'CommRemoteAddress' => @server })
53
+
54
+ when :ipsec
55
+ p['VPNType'] = 'IPSec'
56
+ p_ipsec = { 'AuthenticationMethod' => 'SharedSecret',
57
+ 'LocalIdentifierType' => 'KeyID',
58
+ 'RemoteAddress' => @server }
59
+ p_ipsec['LocalIdentifier'] = @group_name unless @group_name.blank?
60
+ p_ipsec['SharedSecret'] = StringIO.new(@shared_secret) unless @shared_secret.blank?
61
+ unless @username.blank?
62
+ p_ipsec['XAuthEnabled'] = 1
63
+ p_ipsec['XAuthName'] = @username
64
+ end
65
+ p_ipsec['XAuthPasswordEncryption'] = 'Prompt' unless @prompt_for_password.blank?
66
+ p['IPSec'] = p_ipsec
67
+
68
+ when :anyconnect
69
+ p['VPNType'] = 'VPN'
70
+ p['VPNSubType'] = 'com.cisco.anyconnect.applevpn.plugin'
71
+ p['VendorConfig'] = @group_name.blank? ? {} : { 'Group' => @group_name }
72
+ p['VPN'] = generate_vpn_config
73
+
74
+ when :juniper_ssl
75
+ p['VPNType'] = 'VPN'
76
+ p['VPNSubType'] = 'net.juniper.sslvpn'
77
+ vendor_config = {}
78
+ vendor_config['Realm'] = @realm unless @realm.blank?
79
+ vendor_config['Role'] = @role unless @role.blank?
80
+ p['VendorConfig'] = vendor_config
81
+ p['VPN'] = generate_vpn_config
82
+
83
+ when :f5_ssl
84
+ p['VPNType'] = 'VPN'
85
+ p['VPNSubType'] = 'com.f5.F5-Edge-Client.vpnplugin'
86
+ p['VPN'] = generate_vpn_config
87
+ p['VendorConfig'] = {}
88
+
89
+ when :sonicwall_mobile_connect
90
+ p['VPNType'] = 'VPN'
91
+ p['VPNSubType'] = 'com.sonicwall.SonicWALL-SSLVPN.vpnplugin'
92
+ p['VendorConfig'] = @group_or_domain.blank? ? {} : { 'LoginGroupOrDomain' => @group_or_domain }
93
+ p['VPN'] = generate_vpn_config
94
+
95
+ when :aruba_via
96
+ p['VPNType'] = 'VPN'
97
+ p['VPNSubType'] = 'com.arubanetworks.aruba-via.vpnplugin'
98
+ p['VendorConfig'] = {}
99
+ end
100
+
101
+ # Send All Traffic
102
+
103
+ p['OverridePrimary'] = 1 if @send_all_traffic == true
104
+
105
+ # Proxy
106
+
107
+ case @proxy_type
108
+ when :none
109
+ p_proxy = {}
110
+
111
+ when :manual
112
+ p_proxy = { 'HTTPEnable' => 1,
113
+ 'HTTPPort' => @proxy_port,
114
+ 'HTTPProxy' => @proxy_server,
115
+ 'HTTPSEnable' => 1,
116
+ 'HTTPSPort' => @proxy_port,
117
+ 'HTTPSProxy' => @proxy_server }
118
+
119
+ p_proxy['HTTPProxyUsername'] = @proxy_username unless @proxy_username.blank?
120
+ p_proxy['HTTPProxyPassword'] = @proxy_password unless @proxy_password.blank?
121
+
122
+ when :auto
123
+ p_proxy = { 'ProxyAutoConfigEnable' => 1,
124
+ 'ProxyAutoConfigURLString' => @proxy_url }
125
+
126
+ end
127
+ p['Proxies'] = p_proxy
128
+
129
+ p
130
+ end
131
+
132
+ def generate_ppp_config
133
+ p_ppp = { 'CommRemoteAddress' => @server }
134
+ if @authentication_type == :password
135
+ p_ppp['AuthName'] = @username unless @username.blank?
136
+ p_ppp['AuthPassword'] = @password unless @password.blank?
137
+ else
138
+ p_ppp['AuthEAPPlugins'] = ['EAP-RSA']
139
+ p_ppp['AuthProtocol'] = ['EAP']
140
+ end
141
+
142
+ p_ppp
143
+ end
144
+
145
+ def generate_vpn_config
146
+ p_vpn = { 'AuthenticationMethod' => 'Password',
147
+ 'RemoteAddress' => @server }
148
+ p_vpn['AuthName'] = @username unless @username.blank?
149
+ p_vpn['AuthPassword'] = @password unless @password.blank?
150
+
151
+ p_vpn
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,72 @@
1
+ require 'cfpropertylist'
2
+
3
+ module IOSConfig
4
+ class Profile
5
+ require 'openssl'
6
+
7
+ attr_accessor :allow_removal, # if profile can be deleted by device user. defaults to true
8
+ :description, # (optional) displayed in device settings
9
+ :display_name, # displayed in device settings
10
+ :identifier,
11
+ :organization, # (optional) displayed in device settings
12
+ :type, # (optional) default is 'Configuration'
13
+ :uuid,
14
+ :version, # (optional) defaults to '1'
15
+ :payloads, # (optional) payloads to be contained in the profile. should be an array if type is 'Configuration'
16
+ :client_certs # (optional) certificates used to encypt payloads
17
+
18
+ def initialize(options = {})
19
+ options.each { |k,v| self.send("#{k}=", v) }
20
+
21
+ self.allow_removal = true if self.allow_removal.nil?
22
+ self.type ||= 'Configuration'
23
+ self.version ||= 1
24
+ self.payloads ||= []
25
+ end
26
+
27
+ def signed(mdm_cert, mdm_intermediate_cert, mdm_private_key)
28
+ certificate = OpenSSL::X509::Certificate.new(File.read(mdm_cert))
29
+ intermediate = OpenSSL::X509::Certificate.new(File.read(mdm_intermediate_cert))
30
+ private_key = OpenSSL::PKey::RSA.new(File.read(mdm_private_key))
31
+
32
+ signed_profile = OpenSSL::PKCS7.sign(certificate, private_key, unsigned, [intermediate], OpenSSL::PKCS7::BINARY)
33
+ signed_profile.to_der
34
+ end
35
+
36
+ private
37
+
38
+ def unsigned
39
+ raise_if_blank [:version, :uuid, :type, :identifier, :display_name]
40
+
41
+ profile = {
42
+ 'PayloadDisplayName' => self.display_name,
43
+ 'PayloadVersion' => self.version,
44
+ 'PayloadUUID' => self.uuid,
45
+ 'PayloadIdentifier' => self.identifier,
46
+ 'PayloadType' => self.type,
47
+ 'PayloadRemovalDisallowed' => !self.allow_removal
48
+ }
49
+ profile['PayloadOrganization'] = self.organization if self.organization
50
+ profile['PayloadDescription'] = self.description if self.description
51
+
52
+ if self.client_certs.nil?
53
+ profile['PayloadContent'] = payloads
54
+ else
55
+ encrypted_payload_content = OpenSSL::PKCS7.encrypt( self.client_certs,
56
+ payloads.to_plist,
57
+ OpenSSL::Cipher::Cipher::new("des-ede3-cbc"),
58
+ OpenSSL::PKCS7::BINARY)
59
+
60
+ profile['EncryptedPayloadContent'] = StringIO.new encrypted_payload_content.to_der
61
+ end
62
+
63
+ profile.to_plist
64
+ end
65
+
66
+ def raise_if_blank(required_attributes)
67
+ required_attributes.each { |a| raise "#{a} must be set" if self.send(a).blank? }
68
+ end
69
+
70
+ end
71
+
72
+ end
@@ -0,0 +1,3 @@
1
+ module IOSConfig
2
+ VERSION = "1.0.0"
3
+ end
data/lib/ios_config.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'core_ext/string'
2
+ Gem.find_files("ios_config/**/*.rb").each { |path| require path }
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ios_config
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Taylor Boyko
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-07-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: CFPropertyList
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.2'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '2.2'
30
+ - !ruby/object:Gem::Dependency
31
+ name: bundler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '1.3'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '1.3'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Generate configuration profiles and payloads for Apple iOS devices
63
+ email:
64
+ - taylorboyko@gmail.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .DS_Store
70
+ - .gitignore
71
+ - Gemfile
72
+ - LICENSE
73
+ - README.md
74
+ - Rakefile
75
+ - ios_config.gemspec
76
+ - lib/.DS_Store
77
+ - lib/core_ext/string.rb
78
+ - lib/ios_config.rb
79
+ - lib/ios_config/.DS_Store
80
+ - lib/ios_config/payload/base.rb
81
+ - lib/ios_config/payload/vpn.rb
82
+ - lib/ios_config/profile.rb
83
+ - lib/ios_config/version.rb
84
+ homepage: https://github.com/tboyko/ios_config
85
+ licenses:
86
+ - MIT
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ segments:
98
+ - 0
99
+ hash: 2215369409629242488
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ segments:
107
+ - 0
108
+ hash: 2215369409629242488
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 1.8.24
112
+ signing_key:
113
+ specification_version: 3
114
+ summary: This gen provides an easy way to generate profiles and configuration payloads
115
+ for use with Apple iOS devices. These profiles and payloads can be delivered via
116
+ Apple MDM or Apple's Configurator or iPhone Configuration Utility (IPCU).
117
+ test_files: []