require 'spec_helper' describe Mdm::Cred do it_should_behave_like 'Metasploit::Concern.run' context "Associations" do it { should have_many(:task_creds).class_name('Mdm::TaskCred').dependent(:destroy) } it { should have_many(:tasks).class_name('Mdm::Task').through(:task_creds) } it { should belong_to(:service).class_name('Mdm::Service') } end context 'database' do context 'timestamps' do it { should have_db_column(:created_at).of_type(:datetime) } it { should have_db_column(:updated_at).of_type(:datetime) } end context 'columns' do it { should have_db_column(:service_id).of_type(:integer).with_options(:null => false) } it { should have_db_column(:user).of_type(:string) } it { should have_db_column(:pass).of_type(:string) } it { should have_db_column(:active).of_type(:boolean).with_options(:default => true) } it { should have_db_column(:proof).of_type(:string) } it { should have_db_column(:ptype).of_type(:string) } it { should have_db_column(:source_id).of_type(:integer) } it { should have_db_column(:source_type).of_type(:string) } end end context '#destroy' do it 'should successfully destroy the object and all dependent objects' do cred = FactoryGirl.create(:mdm_cred) task_cred = FactoryGirl.create(:mdm_task_cred, :cred => cred) expect { cred.destroy }.to_not raise_error expect { cred.reload }.to raise_error(ActiveRecord::RecordNotFound) expect { task_cred.reload }.to raise_error(ActiveRecord::RecordNotFound) end end context 'callbacks' do context 'after_create' do it 'should increment cred_count on the host' do host = FactoryGirl.create(:mdm_host) svc = FactoryGirl.create(:mdm_service, :host => host) expect { FactoryGirl.create(:mdm_cred, :service => svc) }.to change{ Mdm::Host.find(host.id).cred_count}.by(1) end end context 'after_destroy' do it 'should decrement cred_count on the host' do host = FactoryGirl.create(:mdm_host) svc = FactoryGirl.create(:mdm_service, :host => host) cred =FactoryGirl.create(:mdm_cred, :service => svc) expect { cred.destroy }.to change{ Mdm::Host.find(host.id).cred_count}.by(-1) end end end context 'constants' do it 'should define the key_id regex' do described_class::KEY_ID_REGEX.should == /([0-9a-fA-F:]{47})/ end it 'should define ptypes to humanize' do described_class::PTYPES.should == { 'read/write password' => 'password_rw', 'read-only password' => 'password_ro', 'SMB hash' => 'smb_hash', 'SSH private key' => 'ssh_key', 'SSH public key' => 'ssh_pubkey' } end end context 'methods' do before(:all) do Mdm::Workspace.any_instance.stub(:valid_ip_or_range? => true) workspace = FactoryGirl.create(:mdm_workspace) host = FactoryGirl.create(:mdm_host, :workspace => workspace) @svc1 = FactoryGirl.create(:mdm_service, :host => host) @svc2 = FactoryGirl.create(:mdm_service, :host => host) @cred1 = FactoryGirl.create(:mdm_cred, :service => @svc1, :user => 'msfadmin', :pass => '/path/to/keyfile', :ptype => 'ssh_key', :proof => "KEY=57:c3:11:5d:77:c5:63:90:33:2d:c5:c4:99:78:62:7a") @pubkey = FactoryGirl.create(:mdm_cred, :service => @svc1, :user => 'msfadmin', :pass => '/path/to/keyfile', :ptype => 'ssh_pubkey', :proof => "KEY=57:c3:11:5d:77:c5:63:90:33:2d:c5:c4:99:78:62:7a") end context '#ptype_human' do it "should return 'read/write password' for 'password_rw'" do cred = FactoryGirl.build(:mdm_cred, :user => 'msfadmin', :pass => 'msfadmin', :ptype => 'password_rw') cred.ptype_human.should == 'read/write password' end it "should return 'read-only password' for 'password_ro'" do cred = FactoryGirl.build(:mdm_cred, :user => 'msfadmin', :pass => 'msfadmin', :ptype => 'password_ro') cred.ptype_human.should == 'read-only password' end it "should return 'SMB Hash' for 'smb_hash'" do cred = FactoryGirl.build(:mdm_cred, :user => 'msfadmin', :pass => 'msfadmin', :ptype => 'smb_hash') cred.ptype_human.should == 'SMB hash' end it "should return 'SSH private key' for 'ssh_key'" do cred = FactoryGirl.build(:mdm_cred, :user => 'msfadmin', :pass => 'msfadmin', :ptype => 'ssh_key') cred.ptype_human.should == 'SSH private key' end it "should return 'SSH public key' for 'ssh_pubkey'" do cred = FactoryGirl.build(:mdm_cred, :user => 'msfadmin', :pass => 'msfadmin', :ptype => 'ssh_pubkey') cred.ptype_human.should == 'SSH public key' end end context '#ssh_key_id' do it 'should return nil if not an ssh_key' do cred = FactoryGirl.build(:mdm_cred, :user => 'msfadmin', :pass => 'msfadmin', :ptype => 'password_rw') cred.ssh_key_id.should == nil end it 'should return nil if proof does not contain the key id' do cred = FactoryGirl.build(:mdm_cred, :user => 'msfadmin', :pass => '/path/to/keyfile', :ptype => 'ssh_key', :proof => "no key here") cred.ssh_key_id.should == nil end it 'should return the key id for an ssh_key' do cred = FactoryGirl.build(:mdm_cred, :user => 'msfadmin', :pass => '/path/to/keyfile', :ptype => 'ssh_key', :proof => "KEY=57:c3:11:5d:77:c5:63:90:33:2d:c5:c4:99:78:62:7a") cred.ssh_key_id.should == '57:c3:11:5d:77:c5:63:90:33:2d:c5:c4:99:78:62:7a' end end context '#ssh_key_matches?' do it 'should return true if the ssh_keys match' do cred2 = FactoryGirl.create(:mdm_cred, :service => @svc2, :user => 'msfadmin', :pass => '/path/to/keyfile', :ptype => 'ssh_key', :proof => "KEY=57:c3:11:5d:77:c5:63:90:33:2d:c5:c4:99:78:62:7a") cred2.ssh_key_matches?(@cred1).should == true end it 'should return false if passed something other than a cred' do @cred1.ssh_key_matches?(@svc1).should == false end it 'should return false if the ptypes do not match' do cred2 = FactoryGirl.create(:mdm_cred, :service => @svc2, :user => 'msfadmin', :pass => '/path/to/keyfile', :ptype => 'ssh_pubkey', :proof => "KEY=57:c3:11:5d:77:c5:63:90:33:2d:c5:c4:99:78:62:7a") cred2.ssh_key_matches?(@cred1).should == false end it 'should return false if the key ids do not match' do cred2 = FactoryGirl.create(:mdm_cred, :service => @svc2, :user => 'msfadmin', :pass => '/path/to/keyfile', :ptype => 'ssh_pubkey', :proof => "KEY=66:d4:22:6e:88:d6:74:A1:44:3e:d6:d5:AA:89:73:8b") cred2.ssh_key_matches?(@cred1).should == false end it 'should behave the same for public keys as private keys' do pubkey2 = FactoryGirl.create(:mdm_cred, :service => @svc1, :user => 'msfadmin', :pass => '/path/to/keyfile', :ptype => 'ssh_pubkey', :proof => "KEY=57:c3:11:5d:77:c5:63:90:33:2d:c5:c4:99:78:62:7a") pubkey3 = FactoryGirl.create(:mdm_cred, :service => @svc1, :user => 'msfadmin', :pass => '/path/to/keyfile', :ptype => 'ssh_pubkey', :proof => "KEY=66:d4:22:6e:88:d6:74:A1:44:3e:d6:d5:AA:89:73:8b") pubkey2.ssh_key_matches?(@pubkey).should == true pubkey2.ssh_key_matches?(pubkey3).should == false end it 'should always return false for non ssh key creds' do cred2 = FactoryGirl.create(:mdm_cred, :service => @svc2, :ptype => 'password', :user => 'msfadmin', :pass => 'msfadmin' ) cred3 = FactoryGirl.create(:mdm_cred, :service => @svc2, :ptype => 'password', :user => 'msfadmin', :pass => 'msfadmin' ) cred2.ssh_key_matches?(cred3).should == false end end context '#ssh_keys' do before(:all) do @cred2 = FactoryGirl.create(:mdm_cred, :service => @svc2, :user => 'msfadmin', :pass => '/path/to/keyfile', :ptype => 'ssh_key', :proof => "KEY=57:c3:11:5d:77:c5:63:90:33:2d:c5:c4:99:78:62:7a") end it 'should return all ssh private keys with a matching id' do @cred2.ssh_keys.should include(@cred1) end it 'should return all ssh public keys with a matching id' do @cred2.ssh_keys.should include(@pubkey) end end context '#ssh_private_keys' do before(:all) do @cred2 = FactoryGirl.create(:mdm_cred, :service => @svc2, :user => 'msfadmin', :pass => '/path/to/keyfile', :ptype => 'ssh_key', :proof => "KEY=57:c3:11:5d:77:c5:63:90:33:2d:c5:c4:99:78:62:7a") end it 'should return ssh private keys with matching ids' do @cred2.ssh_private_keys.should include(@cred1) end it 'should not return ssh public keys with matching ids' do @cred2.ssh_private_keys.should_not include(@pubkey) end end context '#ssh_public_keys' do before(:all) do @cred2 = FactoryGirl.create(:mdm_cred, :service => @svc2, :user => 'msfadmin', :pass => '/path/to/keyfile', :ptype => 'ssh_key', :proof => "KEY=57:c3:11:5d:77:c5:63:90:33:2d:c5:c4:99:78:62:7a") end it 'should not return ssh private keys with matching ids' do @cred2.ssh_public_keys.should_not include(@cred1) end it 'should return ssh public keys with matching ids' do @cred2.ssh_public_keys.should include(@pubkey) end end end context 'factory' do it 'should be valid' do cred = FactoryGirl.build(:mdm_cred) cred.should be_valid end end end