macadmin 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a1103e52f907edbf5e1f175278e7eaecb7f5be21
4
+ data.tar.gz: db158590675a6499a351b7ca06c77d0d60078ff8
5
+ SHA512:
6
+ metadata.gz: 898ced3234ca7f09c0271bab0ebcce352c02a01c015349aabcf815a5463b82833e4dd7a94c1e5475b2a65f3f06fc1d937b02a96463df4eae30e46e9a144d7b14
7
+ data.tar.gz: c3e4d8b825d5ec08a684edf1a59c5f40a107b8cb3bcc8bf5567729249bc436e82c064e7c16704de8dd6d84cadcb7caf835e043b43ad35f7ab0ed285ca22bbbf5
@@ -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
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in macadmin.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Brian Warsing
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,163 @@
1
+ # MacAdmin
2
+
3
+ Gem to assist in performing common systems administration tasks in OS X
4
+
5
+ ## Version: 0.0.4
6
+
7
+ ## About
8
+
9
+ MacAdmin endeavors to provide an OO programming interface for constituent OS X system resources. It's comprised of classes with the ability to parse Apple Property Lists (CFPropertyList) and manipulate them as native Ruby objects. The classes work directly with the Property Lists used to abstract OS X system resources -- users, groups, computers, computergroups, etc. -- bypassing the common utilities and APIs normally reserved for this kind of work.
10
+
11
+ This approach has trade-offs, but it does result in a very powerful and simple model for managing these resources (See Notes).
12
+
13
+ #### Notes:
14
+
15
+ Before forking/cloning/using/testing/etc MacAdmin, please read the license (LICENSE.txt).
16
+
17
+ One important trade-off worth mentioning when using this gem is that you must have root priviledge in order to access (read) any resources in the DSLocal domains or similarily protected directories and files. This is different from using utils like `dscl`, but not unlike using `defaults`. Naturally, as with any of these methods, you must also be root in order to make any changes. The code examples below will require root access when performing create operations.
18
+
19
+ Another important condition to mention is that it will often be necessary to restart OS X's directory service in order to see the changes you've made to any of the affected plists. This can be bothersome and susceptible to race conditions, but in general, it's a manageable issue.
20
+
21
+ ## Requirements
22
+
23
+ - Mac OS X 10.5 and up
24
+ - Xcode Command Line Tools
25
+
26
+ ## Installation
27
+
28
+ ### Simple:
29
+
30
+ RubyGems should install any dependencies:
31
+
32
+ $ sudo gem install macadmin
33
+
34
+ ### Manual:
35
+
36
+ Install the bundler gem:
37
+
38
+ $ sudo gem install bundler
39
+
40
+ Clone this repo:
41
+
42
+ $ cd ~/Downloads
43
+ $ git clone http://github.com/dayglojesus/macadmin.git
44
+
45
+ Install the gem dependencies:
46
+
47
+ $ cd macadmin
48
+ $ sudo bundle install
49
+
50
+ Run the tests:
51
+
52
+ $ rake
53
+
54
+ Install the gem:
55
+
56
+ $ sudo rake install
57
+
58
+ Test the installation:
59
+
60
+ ##### Note the path parameter to #create -- use this for testing resource creation in arbitrary directories. Also works for #destroy.
61
+
62
+ $ cd ~/Downloads
63
+ $ irb -r 'macadmin'
64
+ >> foobar = User.new 'foobar'
65
+ => {"passwd"=>["********"], "gid"=>["20"], "uid"=>["501"], "shell"=>["/bin/bash"], "name"=>["foobar"], "realname"=>["foobar"], "generateduid"=>["4871DD7C-5C55-47DB-8A7B-B38CBD6DA5A9"], "comment"=>[""], "home"=>["/Users/foobar"]}
66
+ >> foobar.exists?
67
+ => false
68
+ >> foobar.create "./foobar.plist"
69
+ >> foobar.destroy "./foobar.plist"
70
+ >> exit
71
+
72
+ ## Usage
73
+
74
+ Load the gems:
75
+
76
+ require 'rubygems'
77
+ require 'macadmin'
78
+
79
+ Create a new node:
80
+
81
+ # Here's something cool:
82
+ # DSLocalNode will automatically add this custom node to OpenDirectory sandbox when running on 10.8 and up
83
+ my_custom_node = DSLocalNode.new 'MCX'
84
+ my_custom_node.create_and_activate
85
+
86
+ Create a computer record on that node:
87
+
88
+ computer = Computer.new :name => `hostname -s`.chomp, :node => 'MCX'
89
+ computer.create
90
+
91
+ Create a computer group, add the computer record as a member, and apply some policy (on that node):
92
+
93
+ # Here's a bnuch of policy written as XML, but the mcximport method can also take a file path parameter and load it that way
94
+ raw_xml_policy = <<-RAW_XML_CONTENT
95
+ <?xml version="1.0" encoding="UTF-8"?>
96
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
97
+ <plist version="1.0">
98
+ <dict>
99
+ <key>com.apple.SoftwareUpdate</key>
100
+ <dict>
101
+ <key>CatalogURL</key>
102
+ <dict>
103
+ <key>state</key>
104
+ <string>always</string>
105
+ <key>value</key>
106
+ <string>http://foo.bar.com/reposado/html/content/catalogs/index.sucatalog</string>
107
+ </dict>
108
+ </dict>
109
+ <key>com.apple.screensaver</key>
110
+ <dict>
111
+ <key>askForPassword</key>
112
+ <dict>
113
+ <key>state</key>
114
+ <string>once</string>
115
+ <key>value</key>
116
+ <integer>1</integer>
117
+ </dict>
118
+ </dict>
119
+ </dict>
120
+ </plist>
121
+ RAW_XML_CONTENT
122
+
123
+ computer_group = ComputerGroup.new :name => 'mcx', :realname => 'MCX', :node => 'MCX'
124
+ computer_group.add_user `hostname -s`.chomp
125
+ computer_group.mcximport raw_xml_policy
126
+ computer_group.create
127
+
128
+ Create an administrator for your new node:
129
+
130
+ # Generate a platform appropriate password from a plaintext string
131
+ password = Password.apropos "secret_passphrase"
132
+ administrator = User.new :name => 'mcxadmin', :password => password, :gid => 80, :node => 'MCX'
133
+ administrator.create
134
+
135
+ Restart the directory services to seal the deal:
136
+
137
+ restart_directoryservice
138
+
139
+ Explore a bit...
140
+
141
+ require 'pp'
142
+
143
+ # Show the User object
144
+ admin = User.new :name => 'mcxadmin', :node => 'MCX'
145
+ pp admin.record if admin.exists?
146
+
147
+ # Show the MCX policy attached to the ComputerGroup object
148
+ comp_grp = ComputerGroup.new :name => 'mcx', :node => 'MCX'
149
+ puts "Does this local computer group have MCX policy?"
150
+ puts comp_grp.has_mcx? ? "Sweet!" : "Bummer..."
151
+ puts comp_grp.mcxexport
152
+
153
+ Tear it down...
154
+
155
+ # This will take down the entire node we just created and populated: user, computers, computer groups, etc.
156
+ mcx_node = DSLocalNode.new 'MCX'
157
+ mcx_node.destroy_and_deactivate
158
+
159
+ restart_directoryservice
160
+
161
+ ## Acknowledgments
162
+
163
+ This gem would not be possible without [ckuse's CFPropertyList](https://github.com/ckruse/CFPropertyList). Thanks, Christian.
@@ -0,0 +1,23 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require "macadmin/common"
4
+
5
+ # Only build the extension for Mountian Lion or better
6
+ if MacAdmin::Common::MAC_OS_X_PRODUCT_VERSION > 10.7
7
+
8
+ require "rake/extensiontask"
9
+
10
+ Rake::ExtensionTask.new "crypto" do |ext|
11
+ ext.lib_dir = 'lib/macadmin/password'
12
+ ext.name = 'crypto'
13
+ ext.ext_dir = 'ext/macadmin/password'
14
+ end
15
+
16
+ Rake::Task[:spec].prerequisites << :compile
17
+
18
+ end
19
+
20
+ RSpec::Core::RakeTask.new
21
+
22
+ task :default => :spec
23
+ task :test => :spec
@@ -0,0 +1,89 @@
1
+ //
2
+ // crypto.c
3
+ // MacAdmin::Password::Crypto
4
+ // - Ruby C extension built for fast PBKDF2 calculation
5
+ //
6
+ // Created by Brian Warsing on 2013-10-07.
7
+ // Copyright (c) 2013 Simon Fraser University. All rights reserved.
8
+ //
9
+
10
+ #include <stdio.h>
11
+ #include <stdint.h>
12
+ #include <CommonCrypto/CommonCrypto.h>
13
+ #include "ruby.h"
14
+
15
+ VALUE MacAdmin = Qnil;
16
+ VALUE Password = Qnil;
17
+
18
+ // Prototypes
19
+ void Init_crypto();
20
+ static VALUE salted_sha512_pbkdf2_from_string(VALUE self, VALUE input);
21
+ void to_hex( uint8_t *dest, const uint8_t *text, size_t text_size );
22
+
23
+ // Convert an ASCII char array to hexidecimal representation
24
+ void to_hex( uint8_t *dest, const uint8_t *text, size_t text_size )
25
+ {
26
+ int i;
27
+ for(i = 0; i < text_size; i++)
28
+ sprintf((char*)dest+i*2, "%02x", text[i]);
29
+ }
30
+
31
+ // Ruby Init
32
+ void Init_crypto() {
33
+ MacAdmin = rb_define_module("MacAdmin");
34
+ Password = rb_define_module_under(MacAdmin, "Password");
35
+ rb_define_singleton_method(Password, "salted_sha512_pbkdf2_from_string", salted_sha512_pbkdf2_from_string, 1);
36
+ }
37
+
38
+ // salted_sha512_pbkdf2_from_string
39
+ // - single param: Ruby String
40
+ // - returns Ruby Hash with 3 keys
41
+ // http://blog.securemacprogramming.com/2012/07/password-checking-with-commoncrypto/
42
+ static VALUE salted_sha512_pbkdf2_from_string(VALUE self, VALUE input) {
43
+
44
+ VALUE str = StringValue(input);
45
+ char *password = RSTRING_PTR(str); // may be null
46
+ size_t password_size = RSTRING_LEN(str);
47
+ int salt_len = 32;
48
+
49
+ // Calc how many iterations
50
+ const u_int32_t interval = arc4random_uniform(100) + 100;
51
+ int iterations = CCCalibratePBKDF(kCCPBKDF2,
52
+ password_size,
53
+ salt_len,
54
+ kCCPRFHmacAlgSHA512,
55
+ kCCKeySizeMaxRC2,
56
+ interval);
57
+
58
+ // Generate a salt
59
+ int i;
60
+ uint8_t salt[salt_len];
61
+ for (i = 0; i < salt_len; i++)
62
+ salt[i] = (unsigned char)arc4random();
63
+
64
+ // Generate HMAC
65
+ uint8_t key[kCCKeySizeMaxRC2] = {0};
66
+ int result = CCKeyDerivationPBKDF(kCCPBKDF2,
67
+ password,
68
+ password_size,
69
+ salt,
70
+ salt_len,
71
+ kCCPRFHmacAlgSHA512,
72
+ iterations,
73
+ key,
74
+ kCCKeySizeMaxRC2);
75
+
76
+ uint8_t *entropy[kCCKeySizeMaxRC2];
77
+ to_hex(entropy, key, kCCKeySizeMaxRC2);
78
+
79
+ uint8_t *salt_hex[salt_len];
80
+ to_hex(salt_hex, salt, salt_len);
81
+
82
+ VALUE dict;
83
+ dict = rb_hash_new();
84
+ rb_hash_aset(dict, ID2SYM(rb_intern(("entropy"))), rb_str_new2((char *)entropy));
85
+ rb_hash_aset(dict, ID2SYM(rb_intern(("salt"))), rb_str_new2((char *)salt_hex));
86
+ rb_hash_aset(dict, ID2SYM(rb_intern(("iterations"))), INT2NUM(iterations));
87
+
88
+ return dict;
89
+ }
@@ -0,0 +1,3 @@
1
+ require 'mkmf'
2
+ dir_config('macadmin')
3
+ create_makefile('macadmin/password/crypto')
@@ -0,0 +1,22 @@
1
+ # macadmin gem
2
+ # Sat Aug 11 16:14:56 PDT 2012
3
+ require 'rubygems'
4
+ require 'cgi'
5
+ require 'time'
6
+ require 'delegate'
7
+ require 'fileutils'
8
+ require 'cfpropertylist'
9
+ require 'macadmin/version'
10
+ require 'macadmin/common'
11
+ require 'macadmin/mcx'
12
+ require 'macadmin/password'
13
+ require 'macadmin/shadowhash'
14
+ require 'macadmin/dslocal'
15
+ require 'macadmin/dslocal/user'
16
+ require 'macadmin/dslocal/group'
17
+ require 'macadmin/dslocal/computer'
18
+ require 'macadmin/dslocal/computergroup'
19
+ require 'macadmin/dslocal/dslocalnode'
20
+
21
+ include MacAdmin
22
+ include MacAdmin::Common
@@ -0,0 +1,80 @@
1
+ module MacAdmin
2
+
3
+ # Common
4
+ # - module comtaining classes and methods common to macadmin
5
+ module Common
6
+
7
+ extend self
8
+
9
+ # Mac OS X major version number
10
+ MAC_OS_X_PRODUCT_VERSION = `/usr/bin/sw_vers`.split("\n")[1].split("\t").last.to_f
11
+
12
+ # Class for creating and checking UUID strings
13
+ class UUID < String
14
+
15
+ # 897A6343-628F-4964-80F1-C86D0FFA3F91
16
+ UUID_REGEX = '([A-Z0-9]{8})-([A-Z0-9]{4}-){3}([A-Z0-9]{12})'
17
+
18
+ # Create a new UUID string
19
+ def initialize
20
+ uuid = %x{/usr/bin/uuidgen}.chomp
21
+ super(uuid)
22
+ end
23
+
24
+ # Class methods
25
+ class << self
26
+
27
+ # Matches any string containing a UUID
28
+ # - returns Boolean
29
+ def match(string, options = nil)
30
+ string =~ Regexp.new(UUID_REGEX, options) ? $& : false
31
+ end
32
+
33
+ # Validates the format of a UUID string
34
+ # - only true is the entire string is a UUID match
35
+ # - returns Boolean
36
+ def valid?(uuid, options = nil)
37
+ strictlyuuid = '^' + UUID_REGEX + '$'
38
+ uuid =~ Regexp.new(strictlyuuid, options) ? true : false
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+
45
+ # Get the primary ethernet's MAC address
46
+ # - returns String
47
+ def get_primary_mac_address
48
+ raw = %x{/sbin/ifconfig en0}
49
+ raw.split("\n").grep(/ether/).first.split.last
50
+ end
51
+
52
+ # Load a PropertyList file
53
+ # - convenience method
54
+ # - single parameter is a path to the ProperyList file
55
+ # - returns a Hash is if can parse the file
56
+ # - returns nil if it cannot parse the file
57
+ def load_plist(file)
58
+ return nil unless File.exists? file
59
+ plist = CFPropertyList::List.new(:file => file)
60
+ CFPropertyList.native_types(plist.value)
61
+ end
62
+
63
+ # Restart the local directory service daemon
64
+ # - single param wait (Integer) measured in seconds
65
+ # - default wait is 10 seconds
66
+ def restart_directoryservice(wait=10)
67
+ if MAC_OS_X_PRODUCT_VERSION > 10.6
68
+ system('/usr/bin/killall opendirectoryd')
69
+ else
70
+ system('/usr/bin/killall DirectoryService')
71
+ end
72
+ sleep wait
73
+ end
74
+
75
+ # Alias for convenience
76
+ alias :restart_opendirectoryd :restart_directoryservice
77
+
78
+ end
79
+
80
+ end