Merge branch 'master' into vpc

This commit is contained in:
Alena Prokharchyk 2012-07-09 18:31:28 -07:00
commit 911ed25fbc
52 changed files with 145 additions and 12294 deletions

View File

@ -23,7 +23,6 @@ import java.util.Set;
import org.apache.log4j.Logger;
import com.cloud.api.ApiConstants;
import com.cloud.api.BaseCmd.CommandType;
import com.cloud.api.BaseListTaggedResourcesCmd;
import com.cloud.api.IdentityMapper;
import com.cloud.api.Implementation;

View File

@ -870,7 +870,7 @@ public class EC2Engine {
public boolean associateAddress( EC2AssociateAddress request ) {
try {
CloudStackIpAddress cloudIp = getApi().listPublicIpAddresses(null, null, null, null, null, request.getPublicIp(), null, null, null).get(0);
CloudStackUserVm cloudVm = getApi().listVirtualMachines(null, null, null, null, null, null, request.getInstanceId(), null, null, null, null, null, null, null, null).get(0);
CloudStackUserVm cloudVm = getApi().listVirtualMachines(null, null, true, null, null, null, null, request.getInstanceId(), null, null, null, null, null, null, null, null).get(0);
CloudStackInfoResponse resp = getApi().enableStaticNat(cloudIp.getId(), cloudVm.getId());
if (resp != null) {
@ -1783,7 +1783,7 @@ public class EC2Engine {
throws Exception {
String instId = instanceId != null ? instanceId : null;
List<CloudStackUserVm> vms = getApi().listVirtualMachines(null, null, null, null, null, null,
List<CloudStackUserVm> vms = getApi().listVirtualMachines(null, null, true, null, null, null, null,
instId, null, null, null, null, null, null, null, null);
if(vms != null && vms.size() > 0) {
@ -1912,7 +1912,7 @@ public class EC2Engine {
try {
EC2DescribeSecurityGroupsResponse groupSet = new EC2DescribeSecurityGroupsResponse();
List<CloudStackSecurityGroup> groups = getApi().listSecurityGroups(null, null, null, null, null, null);
List<CloudStackSecurityGroup> groups = getApi().listSecurityGroups(null, null, null, true, null, null, null);
if (groups != null && groups.size() > 0)
for (CloudStackSecurityGroup group : groups) {
boolean matched = false;

View File

@ -311,13 +311,14 @@ public class CloudStackApi {
* @return
* @throws Exception
*/
public List<CloudStackUserVm> listVirtualMachines(String account, String accountId, Boolean forVirtualNetwork, String groupId, String hostId,
public List<CloudStackUserVm> listVirtualMachines(String account, String accountId, Boolean listAll, Boolean forVirtualNetwork, String groupId, String hostId,
String hypervisor, String id, Boolean isRecursive, String keyWord, String name, String networkId, String podId, String state, String storageId,
String zoneId) throws Exception {
CloudStackCommand cmd = new CloudStackCommand(ApiConstants.LIST_VIRTUAL_MACHINES);
if (cmd != null) {
if (account != null) cmd.setParam(ApiConstants.ACCOUNT, account);
if (accountId != null) cmd.setParam(ApiConstants.ACCOUNT_ID, accountId);
if (listAll != null) cmd.setParam(ApiConstants.LIST_ALL, listAll.toString());
if (forVirtualNetwork != null) cmd.setParam(ApiConstants.FOR_VIRTUAL_NETWORK, forVirtualNetwork.toString());
if (groupId != null) cmd.setParam(ApiConstants.GROUP_ID, groupId);
if (hostId != null) cmd.setParam(ApiConstants.HOST_ID, hostId);
@ -1087,13 +1088,14 @@ public class CloudStackApi {
* @return
* @throws Exception
*/
public List<CloudStackSecurityGroup> listSecurityGroups(String account, String domainId, String id, String keyWord, String securityGroupName,
String virtualMachineId) throws Exception {
public List<CloudStackSecurityGroup> listSecurityGroups(String account, String domainId, String id, Boolean listAll, String keyWord,
String securityGroupName, String virtualMachineId) throws Exception {
CloudStackCommand cmd = new CloudStackCommand(ApiConstants.LIST_SECURITY_GROUPS);
if (cmd != null) {
if (account != null) cmd.setParam(ApiConstants.ACCOUNT, account);
if (domainId != null) cmd.setParam(ApiConstants.DOMAIN_ID, domainId);
if (id != null) cmd.setParam(ApiConstants.ID, id);
if (listAll != null) cmd.setParam(ApiConstants.LIST_ALL, listAll.toString());
if (keyWord != null) cmd.setParam(ApiConstants.KEYWORD, keyWord);
if (securityGroupName != null) cmd.setParam(ApiConstants.SECURITY_GROUP_NAME, securityGroupName);
if (virtualMachineId != null) cmd.setParam(ApiConstants.VIRTUAL_MACHINE_ID, virtualMachineId);

View File

@ -242,6 +242,7 @@ public class ApiConstants {
public static final String LINMIN_USERNAME = "linminusername";
public static final String LIST_ACCOUNTS = "listAccounts";
public static final String LIST_ACCOUNTS_RESPONSE = "listaccountsresponse";
public static final String LIST_ALL = "listall";
public static final String LIST_CAPABILITIES = "listCapabilities";
public static final String LIST_CAPABILITIES_RESPONSE = "listcapabilitiesresponse";
public static final String LIST_DISK_OFFERINGS = "listDiskOfferings";

12
build/replace.properties Normal file
View File

@ -0,0 +1,12 @@
DBUSER=cloud
DBPW=cloud
DBROOTPW=
MSLOG=vmops.log
APISERVERLOG=api.log
DBHOST=localhost
AGENTLOGDIR=logs
AGENTLOG=logs/agent.log
MSMNTDIR=/mnt
COMPONENTS-SPEC=components-premium.xml
AWSAPILOG=awsapi.log
REMOTEHOST=localhost

View File

@ -2133,6 +2133,15 @@ public class ApiResponseHelper implements ResponseGenerator {
populateAccount(isoResponse, owner.getId());
populateDomain(isoResponse, owner.getDomainId());
//set tag information
List<? extends ResourceTag> tags = ApiDBUtils.listByResourceTypeAndId(TaggedResourceType.ISO, iso.getId());
List<ResourceTagResponse> tagResponses = new ArrayList<ResourceTagResponse>();
for (ResourceTag tag : tags) {
ResourceTagResponse tagResponse = createResourceTagResponse(tag, true);
tagResponses.add(tagResponse);
}
isoResponse.setTags(tagResponses);
isoResponse.setObjectName("iso");
isoResponses.add(isoResponse);
return isoResponses;
@ -2281,6 +2290,16 @@ public class ApiResponseHelper implements ResponseGenerator {
isoResponse.setSize(isoSize);
}
//set tag information
List<? extends ResourceTag> tags = ApiDBUtils.listByResourceTypeAndId(TaggedResourceType.ISO, iso.getId());
List<ResourceTagResponse> tagResponses = new ArrayList<ResourceTagResponse>();
for (ResourceTag tag : tags) {
ResourceTagResponse tagResponse = createResourceTagResponse(tag, true);
tagResponses.add(tagResponse);
}
isoResponse.setTags(tagResponses);
isoResponse.setObjectName("iso");
isoResponses.add(isoResponse);
return isoResponses;

View File

@ -1296,12 +1296,15 @@ public class ManagementServerImpl implements ManagementServer {
boolean showDomr = ((templateFilter != TemplateFilter.selfexecutable) && (templateFilter != TemplateFilter.featured));
HypervisorType hypervisorType = HypervisorType.getType(cmd.getHypervisor());
return listTemplates(id, cmd.getTemplateName(), cmd.getKeyword(), templateFilter, false, null, cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), hypervisorType, showDomr,
return listTemplates(id, cmd.getTemplateName(), cmd.getKeyword(), templateFilter, false, null,
cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), hypervisorType, showDomr,
cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, tags);
}
private Set<Pair<Long, Long>> listTemplates(Long templateId, String name, String keyword, TemplateFilter templateFilter, boolean isIso, Boolean bootable, Long pageSize, Long startIndex,
Long zoneId, HypervisorType hyperType, boolean showDomr, boolean onlyReady, List<Account> permittedAccounts, Account caller, ListProjectResourcesCriteria listProjectResourcesCriteria, Map<String, String> tags) {
private Set<Pair<Long, Long>> listTemplates(Long templateId, String name, String keyword, TemplateFilter templateFilter,
boolean isIso, Boolean bootable, Long pageSize, Long startIndex, Long zoneId, HypervisorType hyperType,
boolean showDomr, boolean onlyReady, List<Account> permittedAccounts, Account caller,
ListProjectResourcesCriteria listProjectResourcesCriteria, Map<String, String> tags) {
VMTemplateVO template = null;
if (templateId != null) {
@ -1339,11 +1342,12 @@ public class ManagementServerImpl implements ManagementServer {
if (template == null) {
templateZonePairSet = _templateDao.searchSwiftTemplates(name, keyword, templateFilter, isIso,
hypers, bootable, domain, pageSize, startIndex, zoneId, hyperType, onlyReady, showDomr,
permittedAccounts, caller);
permittedAccounts, caller, tags);
Set<Pair<Long, Long>> templateZonePairSet2 = new HashSet<Pair<Long, Long>>();
templateZonePairSet2 = _templateDao.searchTemplates(name, keyword, templateFilter, isIso, hypers,
bootable, domain, pageSize, startIndex, zoneId, hyperType, onlyReady, showDomr,
permittedAccounts, caller, listProjectResourcesCriteria);
permittedAccounts, caller, listProjectResourcesCriteria, tags);
for (Pair<Long, Long> tmpltPair : templateZonePairSet2) {
if (!templateZonePairSet.contains(new Pair<Long, Long>(tmpltPair.first(), -1L))) {
templateZonePairSet.add(tmpltPair);
@ -1362,7 +1366,7 @@ public class ManagementServerImpl implements ManagementServer {
if (template == null) {
templateZonePairSet = _templateDao.searchTemplates(name, keyword, templateFilter, isIso, hypers,
bootable, domain, pageSize, startIndex, zoneId, hyperType, onlyReady, showDomr,
permittedAccounts, caller, listProjectResourcesCriteria);
permittedAccounts, caller, listProjectResourcesCriteria, tags);
} else {
// if template is not public, perform permission check here
if (!template.isPublicTemplate() && caller.getType() != Account.ACCOUNT_TYPE_ADMIN) {

View File

@ -17,6 +17,7 @@
package com.cloud.storage.dao;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.cloud.domain.DomainVO;
@ -40,6 +41,7 @@ public interface VMTemplateDao extends GenericDao<VMTemplateVO, Long> {
//public void update(VMTemplateVO template);
public List<VMTemplateVO> listAllSystemVMTemplates();
public List<VMTemplateVO> listDefaultBuiltinTemplates();
@ -47,17 +49,20 @@ public interface VMTemplateDao extends GenericDao<VMTemplateVO, Long> {
public List<VMTemplateVO> findIsosByIdAndPath(Long domainId, Long accountId, String path);
public List<VMTemplateVO> listReadyTemplates();
public List<VMTemplateVO> listByAccountId(long accountId);
public Set<Pair<Long, Long>> searchTemplates(String name, String keyword, TemplateFilter templateFilter, boolean isIso, List<HypervisorType> hypers, Boolean bootable,
DomainVO domain, Long pageSize, Long startIndex, Long zoneId, HypervisorType hyperType, boolean onlyReady, boolean showDomr, List<Account> permittedAccounts, Account caller, ListProjectResourcesCriteria listProjectResourcesCriteria);
public Set<Pair<Long, Long>> searchTemplates(String name, String keyword, TemplateFilter templateFilter, boolean isIso,
List<HypervisorType> hypers, Boolean bootable, DomainVO domain, Long pageSize, Long startIndex, Long zoneId,
HypervisorType hyperType, boolean onlyReady, boolean showDomr, List<Account> permittedAccounts, Account caller,
ListProjectResourcesCriteria listProjectResourcesCriteria, Map<String, String> tags);
public Set<Pair<Long, Long>> searchSwiftTemplates(String name, String keyword, TemplateFilter templateFilter, boolean isIso, List<HypervisorType> hypers, Boolean bootable, DomainVO domain,
Long pageSize, Long startIndex, Long zoneId, HypervisorType hyperType, boolean onlyReady, boolean showDomr, List<Account> permittedAccounts, Account caller);
public Set<Pair<Long, Long>> searchSwiftTemplates(String name, String keyword, TemplateFilter templateFilter,
boolean isIso, List<HypervisorType> hypers, Boolean bootable, DomainVO domain, Long pageSize, Long startIndex,
Long zoneId, HypervisorType hyperType, boolean onlyReady, boolean showDomr, List<Account> permittedAccounts, Account caller, Map<String, String> tags);
public long addTemplateToZone(VMTemplateVO tmplt, long zoneId);
public List<VMTemplateVO> listAllInZone(long dataCenterId);
public List<VMTemplateVO> listByHypervisorType(List<HypervisorType> hyperTypes);
public List<VMTemplateVO> publicIsoSearch(Boolean bootable, boolean listRemoved);
public List<VMTemplateVO> publicIsoSearch(Boolean bootable, boolean listRemoved, Map<String, String> tags);
VMTemplateVO findSystemVMTemplate(long zoneId);
VMTemplateVO findSystemVMTemplate(long zoneId, HypervisorType hType);

View File

@ -33,7 +33,6 @@ import javax.naming.ConfigurationException;
import org.apache.log4j.Logger;
import com.cloud.api.BaseCmd;
import com.cloud.configuration.Resource;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.domain.DomainVO;
@ -50,6 +49,7 @@ import com.cloud.storage.Storage.TemplateType;
import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.VMTemplateZoneVO;
import com.cloud.tags.ResourceTagVO;
import com.cloud.tags.dao.ResourceTagsDaoImpl;
import com.cloud.template.VirtualMachineTemplate.TemplateFilter;
import com.cloud.user.Account;
@ -139,8 +139,32 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
}
@Override
public List<VMTemplateVO> publicIsoSearch(Boolean bootable, boolean listRemoved){
SearchCriteria<VMTemplateVO> sc = PublicIsoSearch.create();
public List<VMTemplateVO> publicIsoSearch(Boolean bootable, boolean listRemoved, Map<String, String> tags){
SearchBuilder<VMTemplateVO> sb = null;
if (tags == null || tags.isEmpty()) {
sb = PublicIsoSearch;
} else {
sb = createSearchBuilder();
sb.and("public", sb.entity().isPublicTemplate(), SearchCriteria.Op.EQ);
sb.and("format", sb.entity().getFormat(), SearchCriteria.Op.EQ);
sb.and("type", sb.entity().getTemplateType(), SearchCriteria.Op.EQ);
sb.and("bootable", sb.entity().isBootable(), SearchCriteria.Op.EQ);
sb.and("removed", sb.entity().getRemoved(), SearchCriteria.Op.EQ);
SearchBuilder<ResourceTagVO> tagSearch = _tagsDao.createSearchBuilder();
for (int count=0; count < tags.size(); count++) {
tagSearch.or().op("key" + String.valueOf(count), tagSearch.entity().getKey(), SearchCriteria.Op.EQ);
tagSearch.and("value" + String.valueOf(count), tagSearch.entity().getValue(), SearchCriteria.Op.EQ);
tagSearch.cp();
}
tagSearch.and("resourceType", tagSearch.entity().getResourceType(), SearchCriteria.Op.EQ);
sb.groupBy(sb.entity().getId());
sb.join("tagSearch", tagSearch, sb.entity().getId(), tagSearch.entity().getResourceId(), JoinBuilder.JoinType.INNER);
}
SearchCriteria<VMTemplateVO> sc = sb.create();
sc.setParameters("public", 1);
sc.setParameters("format", "ISO");
sc.setParameters("type", TemplateType.PERHOST.toString());
@ -152,6 +176,16 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
sc.setParameters("removed", (Object)null);
}
if (tags != null && !tags.isEmpty()) {
int count = 0;
sc.setJoinParameters("tagSearch", "resourceType", TaggedResourceType.ISO.toString());
for (String key : tags.keySet()) {
sc.setJoinParameters("tagSearch", "key" + String.valueOf(count), key);
sc.setJoinParameters("tagSearch", "value" + String.valueOf(count), tags.get(key));
count++;
}
}
return listBy(sc);
}
@ -265,7 +299,6 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
PublicIsoSearch.and("bootable", PublicIsoSearch.entity().isBootable(), SearchCriteria.Op.EQ);
PublicIsoSearch.and("removed", PublicIsoSearch.entity().getRemoved(), SearchCriteria.Op.EQ);
tmpltTypeHyperSearch = createSearchBuilder();
tmpltTypeHyperSearch.and("templateType", tmpltTypeHyperSearch.entity().getTemplateType(), SearchCriteria.Op.EQ);
SearchBuilder<HostVO> hostHyperSearch = _hostDao.createSearchBuilder();
@ -320,7 +353,7 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
@Override
public Set<Pair<Long, Long>> searchSwiftTemplates(String name, String keyword, TemplateFilter templateFilter, boolean isIso, List<HypervisorType> hypers, Boolean bootable, DomainVO domain,
Long pageSize, Long startIndex, Long zoneId, HypervisorType hyperType, boolean onlyReady, boolean showDomr, List<Account> permittedAccounts, Account caller) {
Long pageSize, Long startIndex, Long zoneId, HypervisorType hyperType, boolean onlyReady, boolean showDomr, List<Account> permittedAccounts, Account caller, Map<String, String> tags) {
StringBuilder builder = new StringBuilder();
if (!permittedAccounts.isEmpty()) {
@ -436,8 +469,12 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
return templateZonePairList;
}
@Override
public Set<Pair<Long, Long>> searchTemplates(String name, String keyword, TemplateFilter templateFilter, boolean isIso, List<HypervisorType> hypers, Boolean bootable, DomainVO domain, Long pageSize, Long startIndex, Long zoneId, HypervisorType hyperType, boolean onlyReady, boolean showDomr,List<Account> permittedAccounts, Account caller, ListProjectResourcesCriteria listProjectResourcesCriteria) {
public Set<Pair<Long, Long>> searchTemplates(String name, String keyword, TemplateFilter templateFilter,
boolean isIso, List<HypervisorType> hypers, Boolean bootable, DomainVO domain, Long pageSize, Long startIndex,
Long zoneId, HypervisorType hyperType, boolean onlyReady, boolean showDomr,List<Account> permittedAccounts,
Account caller, ListProjectResourcesCriteria listProjectResourcesCriteria, Map<String, String> tags) {
StringBuilder builder = new StringBuilder();
if (!permittedAccounts.isEmpty()) {
for (Account permittedAccount : permittedAccounts) {
@ -468,6 +505,7 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
String guestOSJoin = "";
StringBuilder templateHostRefJoin = new StringBuilder();
String dataCenterJoin = "", lpjoin = "";
String tagsJoin = "";
if (isIso && !hyperType.equals(HypervisorType.None)) {
guestOSJoin = " INNER JOIN guest_os guestOS on (guestOS.id = t.guest_os_id) INNER JOIN guest_os_hypervisor goh on ( goh.guest_os_id = guestOS.id) ";
@ -480,11 +518,16 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
if ((templateFilter == TemplateFilter.featured) || (templateFilter == TemplateFilter.community)) {
dataCenterJoin = " INNER JOIN data_center dc on (h.data_center_id = dc.id)";
}
if (templateFilter == TemplateFilter.sharedexecutable){
lpjoin = " INNER JOIN launch_permission lp ON t.id = lp.template_id ";
}
sql += guestOSJoin + templateHostRefJoin + dataCenterJoin + lpjoin;
if (tags != null && !tags.isEmpty()) {
tagsJoin = " INNER JOIN resource_tags r ON t.id = r.resource_id ";
}
sql += guestOSJoin + templateHostRefJoin + dataCenterJoin + lpjoin + tagsJoin;
String whereClause = "";
//All joins have to be made before we start setting the condition settings
@ -497,7 +540,7 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
if (listProjectResourcesCriteria == ListProjectResourcesCriteria.SkipProjectResources) {
whereClause += " AND a.type != " + Account.ACCOUNT_TYPE_PROJECT;
}
}else
} else
if (listProjectResourcesCriteria == ListProjectResourcesCriteria.SkipProjectResources) {
whereClause += " WHERE a.type != " + Account.ACCOUNT_TYPE_PROJECT;
}
@ -581,13 +624,27 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
return templateZonePairList;
}
if (tags != null && !tags.isEmpty()) {
whereClause += " AND (";
boolean first = true;
for (String key : tags.keySet()) {
if (!first) {
whereClause += " OR ";
}
whereClause += "(r.key=\"" + key + "\" and r.value=\"" + tags.get(key) + "\")";
first = false;
}
whereClause += ")";
}
if (whereClause.equals("")) {
whereClause += " WHERE ";
} else if (!whereClause.equals(" WHERE ")) {
whereClause += " AND ";
}
sql += whereClause + getExtrasWhere(templateFilter, name, keyword, isIso, bootable, hyperType, zoneId, onlyReady, showDomr) + groupByClause + getOrderByLimit(pageSize, startIndex);
sql += whereClause + getExtrasWhere(templateFilter, name, keyword, isIso, bootable, hyperType, zoneId,
onlyReady, showDomr) + groupByClause + getOrderByLimit(pageSize, startIndex);
pstmt = txn.prepareStatement(sql);
rs = pstmt.executeQuery();
@ -600,7 +657,7 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
if(isIso && templateZonePairList.size() < (pageSize != null ? pageSize : 500)
&& templateFilter != TemplateFilter.community
&& !(templateFilter == TemplateFilter.self && !BaseCmd.isRootAdmin(caller.getType())) ){ //evaluates to true If root admin and filter=self
List<VMTemplateVO> publicIsos = publicIsoSearch(bootable, false);
List<VMTemplateVO> publicIsos = publicIsoSearch(bootable, false, tags);
for( int i=0; i < publicIsos.size(); i++){
if (keyword != null && publicIsos.get(i).getName().contains(keyword)) {
templateZonePairList.add(new Pair<Long,Long>(publicIsos.get(i).getId(), null));

View File

@ -323,9 +323,13 @@ public class TaggedResourceManagerImpl implements TaggedResourceService, Manager
sb.and("key", sb.entity().getKey(), SearchCriteria.Op.EQ);
sb.and("value", sb.entity().getValue(), SearchCriteria.Op.EQ);
if (resourceId != null) {
sb.and().op("resourceId", sb.entity().getResourceId(), SearchCriteria.Op.EQ);
sb.or("resourceUuid", sb.entity().getResourceUuid(), SearchCriteria.Op.EQ);
sb.cp();
}
sb.and("resourceType", sb.entity().getResourceType(), SearchCriteria.Op.EQ);
sb.and("customer", sb.entity().getCustomer(), SearchCriteria.Op.EQ);

View File

@ -2318,9 +2318,8 @@ CREATE TABLE `cloud`.`resource_tags` (
CONSTRAINT `fk_tags__account_id` FOREIGN KEY(`account_id`) REFERENCES `account`(`id`),
CONSTRAINT `fk_tags__domain_id` FOREIGN KEY(`domain_id`) REFERENCES `domain`(`id`),
UNIQUE `i_tags__resource_id__resource_type__key`(`resource_id`, `resource_type`, `key`),
CONSTRAINT `uc_resource_tags__uuid` UNIQUE (`uuid`),
CONSTRAINT `uc_resource_tags__resource_uuid` UNIQUE (`resource_uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CONSTRAINT `uc_resource_tags__uuid` UNIQUE (`uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
SET foreign_key_checks = 1;

View File

@ -1,138 +0,0 @@
# Copyright (C) 2003-2009 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
I{Paramiko} (a combination of the esperanto words for "paranoid" and "friend")
is a module for python 2.3 or greater that implements the SSH2 protocol for
secure (encrypted and authenticated) connections to remote machines. Unlike
SSL (aka TLS), the SSH2 protocol does not require heirarchical certificates
signed by a powerful central authority. You may know SSH2 as the protocol that
replaced C{telnet} and C{rsh} for secure access to remote shells, but the
protocol also includes the ability to open arbitrary channels to remote
services across an encrypted tunnel. (This is how C{sftp} works, for example.)
The high-level client API starts with creation of an L{SSHClient} object.
For more direct control, pass a socket (or socket-like object) to a
L{Transport}, and use L{start_server <Transport.start_server>} or
L{start_client <Transport.start_client>} to negoatite
with the remote host as either a server or client. As a client, you are
responsible for authenticating using a password or private key, and checking
the server's host key. I{(Key signature and verification is done by paramiko,
but you will need to provide private keys and check that the content of a
public key matches what you expected to see.)} As a server, you are
responsible for deciding which users, passwords, and keys to allow, and what
kind of channels to allow.
Once you have finished, either side may request flow-controlled L{Channel}s to
the other side, which are python objects that act like sockets, but send and
receive data over the encrypted session.
Paramiko is written entirely in python (no C or platform-dependent code) and is
released under the GNU Lesser General Public License (LGPL).
Website: U{http://www.lag.net/paramiko/}
@version: 1.7.6 (Fanny)
@author: Robey Pointer
@contact: robeypointer@gmail.com
@license: GNU Lesser General Public License (LGPL)
"""
import sys
if sys.version_info < (2, 2):
raise RuntimeError('You need python 2.2 for this module.')
__author__ = "Robey Pointer <robeypointer@gmail.com>"
__date__ = "1 Nov 2009"
__version__ = "1.7.6 (Fanny)"
__version_info__ = (1, 7, 6)
__license__ = "GNU Lesser General Public License (LGPL)"
from transport import randpool, SecurityOptions, Transport
from client import SSHClient, MissingHostKeyPolicy, AutoAddPolicy, RejectPolicy, WarningPolicy
from auth_handler import AuthHandler
from channel import Channel, ChannelFile
from ssh_exception import SSHException, PasswordRequiredException, \
BadAuthenticationType, ChannelException, BadHostKeyException, \
AuthenticationException
from server import ServerInterface, SubsystemHandler, InteractiveQuery
from rsakey import RSAKey
from dsskey import DSSKey
from sftp import SFTPError, BaseSFTP
from sftp_client import SFTP, SFTPClient
from sftp_server import SFTPServer
from sftp_attr import SFTPAttributes
from sftp_handle import SFTPHandle
from sftp_si import SFTPServerInterface
from sftp_file import SFTPFile
from message import Message
from packet import Packetizer
from file import BufferedFile
from agent import Agent, AgentKey
from pkey import PKey
from hostkeys import HostKeys
from config import SSHConfig
# fix module names for epydoc
for c in locals().values():
if issubclass(type(c), type) or type(c).__name__ == 'classobj':
# classobj for exceptions :/
c.__module__ = __name__
del c
from common import AUTH_SUCCESSFUL, AUTH_PARTIALLY_SUCCESSFUL, AUTH_FAILED, \
OPEN_SUCCEEDED, OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED, OPEN_FAILED_CONNECT_FAILED, \
OPEN_FAILED_UNKNOWN_CHANNEL_TYPE, OPEN_FAILED_RESOURCE_SHORTAGE
from sftp import SFTP_OK, SFTP_EOF, SFTP_NO_SUCH_FILE, SFTP_PERMISSION_DENIED, SFTP_FAILURE, \
SFTP_BAD_MESSAGE, SFTP_NO_CONNECTION, SFTP_CONNECTION_LOST, SFTP_OP_UNSUPPORTED
__all__ = [ 'Transport',
'SSHClient',
'MissingHostKeyPolicy',
'AutoAddPolicy',
'RejectPolicy',
'WarningPolicy',
'SecurityOptions',
'SubsystemHandler',
'Channel',
'PKey',
'RSAKey',
'DSSKey',
'Message',
'SSHException',
'AuthenticationException',
'PasswordRequiredException',
'BadAuthenticationType',
'ChannelException',
'BadHostKeyException',
'SFTP',
'SFTPFile',
'SFTPHandle',
'SFTPClient',
'SFTPServer',
'SFTPError',
'SFTPAttributes',
'SFTPServerInterface',
'ServerInterface',
'BufferedFile',
'Agent',
'AgentKey',
'HostKeys',
'SSHConfig',
'util' ]

View File

@ -1,148 +0,0 @@
# Copyright (C) 2003-2007 John Rochester <john@jrochester.org>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
SSH Agent interface for Unix clients.
"""
import os
import socket
import struct
import sys
from paramiko.ssh_exception import SSHException
from paramiko.message import Message
from paramiko.pkey import PKey
SSH2_AGENTC_REQUEST_IDENTITIES, SSH2_AGENT_IDENTITIES_ANSWER, \
SSH2_AGENTC_SIGN_REQUEST, SSH2_AGENT_SIGN_RESPONSE = range(11, 15)
class Agent:
"""
Client interface for using private keys from an SSH agent running on the
local machine. If an SSH agent is running, this class can be used to
connect to it and retreive L{PKey} objects which can be used when
attempting to authenticate to remote SSH servers.
Because the SSH agent protocol uses environment variables and unix-domain
sockets, this probably doesn't work on Windows. It does work on most
posix platforms though (Linux and MacOS X, for example).
"""
def __init__(self):
"""
Open a session with the local machine's SSH agent, if one is running.
If no agent is running, initialization will succeed, but L{get_keys}
will return an empty tuple.
@raise SSHException: if an SSH agent is found, but speaks an
incompatible protocol
"""
self.keys = ()
if ('SSH_AUTH_SOCK' in os.environ) and (sys.platform != 'win32'):
conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
conn.connect(os.environ['SSH_AUTH_SOCK'])
except:
# probably a dangling env var: the ssh agent is gone
return
self.conn = conn
elif sys.platform == 'win32':
import win_pageant
if win_pageant.can_talk_to_agent():
self.conn = win_pageant.PageantConnection()
else:
return
else:
# no agent support
return
ptype, result = self._send_message(chr(SSH2_AGENTC_REQUEST_IDENTITIES))
if ptype != SSH2_AGENT_IDENTITIES_ANSWER:
raise SSHException('could not get keys from ssh-agent')
keys = []
for i in range(result.get_int()):
keys.append(AgentKey(self, result.get_string()))
result.get_string()
self.keys = tuple(keys)
def close(self):
"""
Close the SSH agent connection.
"""
self.conn.close()
self.conn = None
self.keys = ()
def get_keys(self):
"""
Return the list of keys available through the SSH agent, if any. If
no SSH agent was running (or it couldn't be contacted), an empty list
will be returned.
@return: a list of keys available on the SSH agent
@rtype: tuple of L{AgentKey}
"""
return self.keys
def _send_message(self, msg):
msg = str(msg)
self.conn.send(struct.pack('>I', len(msg)) + msg)
l = self._read_all(4)
msg = Message(self._read_all(struct.unpack('>I', l)[0]))
return ord(msg.get_byte()), msg
def _read_all(self, wanted):
result = self.conn.recv(wanted)
while len(result) < wanted:
if len(result) == 0:
raise SSHException('lost ssh-agent')
extra = self.conn.recv(wanted - len(result))
if len(extra) == 0:
raise SSHException('lost ssh-agent')
result += extra
return result
class AgentKey(PKey):
"""
Private key held in a local SSH agent. This type of key can be used for
authenticating to a remote server (signing). Most other key operations
work as expected.
"""
def __init__(self, agent, blob):
self.agent = agent
self.blob = blob
self.name = Message(blob).get_string()
def __str__(self):
return self.blob
def get_name(self):
return self.name
def sign_ssh_data(self, randpool, data):
msg = Message()
msg.add_byte(chr(SSH2_AGENTC_SIGN_REQUEST))
msg.add_string(self.blob)
msg.add_string(data)
msg.add_int(0)
ptype, result = self.agent._send_message(msg)
if ptype != SSH2_AGENT_SIGN_RESPONSE:
raise SSHException('key cannot be used for signing')
return result.get_string()

View File

@ -1,423 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
L{AuthHandler}
"""
import threading
import weakref
# this helps freezing utils
import encodings.utf_8
from paramiko.common import *
from paramiko import util
from paramiko.message import Message
from paramiko.ssh_exception import SSHException, AuthenticationException, \
BadAuthenticationType, PartialAuthentication
from paramiko.server import InteractiveQuery
class AuthHandler (object):
"""
Internal class to handle the mechanics of authentication.
"""
def __init__(self, transport):
self.transport = weakref.proxy(transport)
self.username = None
self.authenticated = False
self.auth_event = None
self.auth_method = ''
self.password = None
self.private_key = None
self.interactive_handler = None
self.submethods = None
# for server mode:
self.auth_username = None
self.auth_fail_count = 0
def is_authenticated(self):
return self.authenticated
def get_username(self):
if self.transport.server_mode:
return self.auth_username
else:
return self.username
def auth_none(self, username, event):
self.transport.lock.acquire()
try:
self.auth_event = event
self.auth_method = 'none'
self.username = username
self._request_auth()
finally:
self.transport.lock.release()
def auth_publickey(self, username, key, event):
self.transport.lock.acquire()
try:
self.auth_event = event
self.auth_method = 'publickey'
self.username = username
self.private_key = key
self._request_auth()
finally:
self.transport.lock.release()
def auth_password(self, username, password, event):
self.transport.lock.acquire()
try:
self.auth_event = event
self.auth_method = 'password'
self.username = username
self.password = password
self._request_auth()
finally:
self.transport.lock.release()
def auth_interactive(self, username, handler, event, submethods=''):
"""
response_list = handler(title, instructions, prompt_list)
"""
self.transport.lock.acquire()
try:
self.auth_event = event
self.auth_method = 'keyboard-interactive'
self.username = username
self.interactive_handler = handler
self.submethods = submethods
self._request_auth()
finally:
self.transport.lock.release()
def abort(self):
if self.auth_event is not None:
self.auth_event.set()
### internals...
def _request_auth(self):
m = Message()
m.add_byte(chr(MSG_SERVICE_REQUEST))
m.add_string('ssh-userauth')
self.transport._send_message(m)
def _disconnect_service_not_available(self):
m = Message()
m.add_byte(chr(MSG_DISCONNECT))
m.add_int(DISCONNECT_SERVICE_NOT_AVAILABLE)
m.add_string('Service not available')
m.add_string('en')
self.transport._send_message(m)
self.transport.close()
def _disconnect_no_more_auth(self):
m = Message()
m.add_byte(chr(MSG_DISCONNECT))
m.add_int(DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE)
m.add_string('No more auth methods available')
m.add_string('en')
self.transport._send_message(m)
self.transport.close()
def _get_session_blob(self, key, service, username):
m = Message()
m.add_string(self.transport.session_id)
m.add_byte(chr(MSG_USERAUTH_REQUEST))
m.add_string(username)
m.add_string(service)
m.add_string('publickey')
m.add_boolean(1)
m.add_string(key.get_name())
m.add_string(str(key))
return str(m)
def wait_for_response(self, event):
while True:
event.wait(0.1)
if not self.transport.is_active():
e = self.transport.get_exception()
if (e is None) or issubclass(e.__class__, EOFError):
e = AuthenticationException('Authentication failed.')
raise e
if event.isSet():
break
if not self.is_authenticated():
e = self.transport.get_exception()
if e is None:
e = AuthenticationException('Authentication failed.')
# this is horrible. python Exception isn't yet descended from
# object, so type(e) won't work. :(
if issubclass(e.__class__, PartialAuthentication):
return e.allowed_types
raise e
return []
def _parse_service_request(self, m):
service = m.get_string()
if self.transport.server_mode and (service == 'ssh-userauth'):
# accepted
m = Message()
m.add_byte(chr(MSG_SERVICE_ACCEPT))
m.add_string(service)
self.transport._send_message(m)
return
# dunno this one
self._disconnect_service_not_available()
def _parse_service_accept(self, m):
service = m.get_string()
if service == 'ssh-userauth':
self.transport._log(DEBUG, 'userauth is OK')
m = Message()
m.add_byte(chr(MSG_USERAUTH_REQUEST))
m.add_string(self.username)
m.add_string('ssh-connection')
m.add_string(self.auth_method)
if self.auth_method == 'password':
m.add_boolean(False)
password = self.password
if isinstance(password, unicode):
password = password.encode('UTF-8')
m.add_string(password)
elif self.auth_method == 'publickey':
m.add_boolean(True)
m.add_string(self.private_key.get_name())
m.add_string(str(self.private_key))
blob = self._get_session_blob(self.private_key, 'ssh-connection', self.username)
sig = self.private_key.sign_ssh_data(self.transport.randpool, blob)
m.add_string(str(sig))
elif self.auth_method == 'keyboard-interactive':
m.add_string('')
m.add_string(self.submethods)
elif self.auth_method == 'none':
pass
else:
raise SSHException('Unknown auth method "%s"' % self.auth_method)
self.transport._send_message(m)
else:
self.transport._log(DEBUG, 'Service request "%s" accepted (?)' % service)
def _send_auth_result(self, username, method, result):
# okay, send result
m = Message()
if result == AUTH_SUCCESSFUL:
self.transport._log(INFO, 'Auth granted (%s).' % method)
m.add_byte(chr(MSG_USERAUTH_SUCCESS))
self.authenticated = True
else:
self.transport._log(INFO, 'Auth rejected (%s).' % method)
m.add_byte(chr(MSG_USERAUTH_FAILURE))
m.add_string(self.transport.server_object.get_allowed_auths(username))
if result == AUTH_PARTIALLY_SUCCESSFUL:
m.add_boolean(1)
else:
m.add_boolean(0)
self.auth_fail_count += 1
self.transport._send_message(m)
if self.auth_fail_count >= 10:
self._disconnect_no_more_auth()
if result == AUTH_SUCCESSFUL:
self.transport._auth_trigger()
def _interactive_query(self, q):
# make interactive query instead of response
m = Message()
m.add_byte(chr(MSG_USERAUTH_INFO_REQUEST))
m.add_string(q.name)
m.add_string(q.instructions)
m.add_string('')
m.add_int(len(q.prompts))
for p in q.prompts:
m.add_string(p[0])
m.add_boolean(p[1])
self.transport._send_message(m)
def _parse_userauth_request(self, m):
if not self.transport.server_mode:
# er, uh... what?
m = Message()
m.add_byte(chr(MSG_USERAUTH_FAILURE))
m.add_string('none')
m.add_boolean(0)
self.transport._send_message(m)
return
if self.authenticated:
# ignore
return
username = m.get_string()
service = m.get_string()
method = m.get_string()
self.transport._log(DEBUG, 'Auth request (type=%s) service=%s, username=%s' % (method, service, username))
if service != 'ssh-connection':
self._disconnect_service_not_available()
return
if (self.auth_username is not None) and (self.auth_username != username):
self.transport._log(WARNING, 'Auth rejected because the client attempted to change username in mid-flight')
self._disconnect_no_more_auth()
return
self.auth_username = username
if method == 'none':
result = self.transport.server_object.check_auth_none(username)
elif method == 'password':
changereq = m.get_boolean()
password = m.get_string()
try:
password = password.decode('UTF-8')
except UnicodeError:
# some clients/servers expect non-utf-8 passwords!
# in this case, just return the raw byte string.
pass
if changereq:
# always treated as failure, since we don't support changing passwords, but collect
# the list of valid auth types from the callback anyway
self.transport._log(DEBUG, 'Auth request to change passwords (rejected)')
newpassword = m.get_string()
try:
newpassword = newpassword.decode('UTF-8', 'replace')
except UnicodeError:
pass
result = AUTH_FAILED
else:
result = self.transport.server_object.check_auth_password(username, password)
elif method == 'publickey':
sig_attached = m.get_boolean()
keytype = m.get_string()
keyblob = m.get_string()
try:
key = self.transport._key_info[keytype](Message(keyblob))
except SSHException, e:
self.transport._log(INFO, 'Auth rejected: public key: %s' % str(e))
key = None
except:
self.transport._log(INFO, 'Auth rejected: unsupported or mangled public key')
key = None
if key is None:
self._disconnect_no_more_auth()
return
# first check if this key is okay... if not, we can skip the verify
result = self.transport.server_object.check_auth_publickey(username, key)
if result != AUTH_FAILED:
# key is okay, verify it
if not sig_attached:
# client wants to know if this key is acceptable, before it
# signs anything... send special "ok" message
m = Message()
m.add_byte(chr(MSG_USERAUTH_PK_OK))
m.add_string(keytype)
m.add_string(keyblob)
self.transport._send_message(m)
return
sig = Message(m.get_string())
blob = self._get_session_blob(key, service, username)
if not key.verify_ssh_sig(blob, sig):
self.transport._log(INFO, 'Auth rejected: invalid signature')
result = AUTH_FAILED
elif method == 'keyboard-interactive':
lang = m.get_string()
submethods = m.get_string()
result = self.transport.server_object.check_auth_interactive(username, submethods)
if isinstance(result, InteractiveQuery):
# make interactive query instead of response
self._interactive_query(result)
return
else:
result = self.transport.server_object.check_auth_none(username)
# okay, send result
self._send_auth_result(username, method, result)
def _parse_userauth_success(self, m):
self.transport._log(INFO, 'Authentication (%s) successful!' % self.auth_method)
self.authenticated = True
self.transport._auth_trigger()
if self.auth_event != None:
self.auth_event.set()
def _parse_userauth_failure(self, m):
authlist = m.get_list()
partial = m.get_boolean()
if partial:
self.transport._log(INFO, 'Authentication continues...')
self.transport._log(DEBUG, 'Methods: ' + str(authlist))
self.transport.saved_exception = PartialAuthentication(authlist)
elif self.auth_method not in authlist:
self.transport._log(DEBUG, 'Authentication type (%s) not permitted.' % self.auth_method)
self.transport._log(DEBUG, 'Allowed methods: ' + str(authlist))
self.transport.saved_exception = BadAuthenticationType('Bad authentication type', authlist)
else:
self.transport._log(INFO, 'Authentication (%s) failed.' % self.auth_method)
self.authenticated = False
self.username = None
if self.auth_event != None:
self.auth_event.set()
def _parse_userauth_banner(self, m):
banner = m.get_string()
lang = m.get_string()
self.transport._log(INFO, 'Auth banner: ' + banner)
# who cares.
def _parse_userauth_info_request(self, m):
if self.auth_method != 'keyboard-interactive':
raise SSHException('Illegal info request from server')
title = m.get_string()
instructions = m.get_string()
m.get_string() # lang
prompts = m.get_int()
prompt_list = []
for i in range(prompts):
prompt_list.append((m.get_string(), m.get_boolean()))
response_list = self.interactive_handler(title, instructions, prompt_list)
m = Message()
m.add_byte(chr(MSG_USERAUTH_INFO_RESPONSE))
m.add_int(len(response_list))
for r in response_list:
m.add_string(r)
self.transport._send_message(m)
def _parse_userauth_info_response(self, m):
if not self.transport.server_mode:
raise SSHException('Illegal info response from server')
n = m.get_int()
responses = []
for i in range(n):
responses.append(m.get_string())
result = self.transport.server_object.check_auth_interactive_response(responses)
if isinstance(type(result), InteractiveQuery):
# make interactive query instead of response
self._interactive_query(result)
return
self._send_auth_result(self.auth_username, 'keyboard-interactive', result)
_handler_table = {
MSG_SERVICE_REQUEST: _parse_service_request,
MSG_SERVICE_ACCEPT: _parse_service_accept,
MSG_USERAUTH_REQUEST: _parse_userauth_request,
MSG_USERAUTH_SUCCESS: _parse_userauth_success,
MSG_USERAUTH_FAILURE: _parse_userauth_failure,
MSG_USERAUTH_BANNER: _parse_userauth_banner,
MSG_USERAUTH_INFO_REQUEST: _parse_userauth_info_request,
MSG_USERAUTH_INFO_RESPONSE: _parse_userauth_info_response,
}

View File

@ -1,126 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
import util
class BERException (Exception):
pass
class BER(object):
"""
Robey's tiny little attempt at a BER decoder.
"""
def __init__(self, content=''):
self.content = content
self.idx = 0
def __str__(self):
return self.content
def __repr__(self):
return 'BER(\'' + repr(self.content) + '\')'
def decode(self):
return self.decode_next()
def decode_next(self):
if self.idx >= len(self.content):
return None
ident = ord(self.content[self.idx])
self.idx += 1
if (ident & 31) == 31:
# identifier > 30
ident = 0
while self.idx < len(self.content):
t = ord(self.content[self.idx])
self.idx += 1
ident = (ident << 7) | (t & 0x7f)
if not (t & 0x80):
break
if self.idx >= len(self.content):
return None
# now fetch length
size = ord(self.content[self.idx])
self.idx += 1
if size & 0x80:
# more complimicated...
# FIXME: theoretically should handle indefinite-length (0x80)
t = size & 0x7f
if self.idx + t > len(self.content):
return None
size = util.inflate_long(self.content[self.idx : self.idx + t], True)
self.idx += t
if self.idx + size > len(self.content):
# can't fit
return None
data = self.content[self.idx : self.idx + size]
self.idx += size
# now switch on id
if ident == 0x30:
# sequence
return self.decode_sequence(data)
elif ident == 2:
# int
return util.inflate_long(data)
else:
# 1: boolean (00 false, otherwise true)
raise BERException('Unknown ber encoding type %d (robey is lazy)' % ident)
def decode_sequence(data):
out = []
b = BER(data)
while True:
x = b.decode_next()
if x is None:
break
out.append(x)
return out
decode_sequence = staticmethod(decode_sequence)
def encode_tlv(self, ident, val):
# no need to support ident > 31 here
self.content += chr(ident)
if len(val) > 0x7f:
lenstr = util.deflate_long(len(val))
self.content += chr(0x80 + len(lenstr)) + lenstr
else:
self.content += chr(len(val))
self.content += val
def encode(self, x):
if type(x) is bool:
if x:
self.encode_tlv(1, '\xff')
else:
self.encode_tlv(1, '\x00')
elif (type(x) is int) or (type(x) is long):
self.encode_tlv(2, util.deflate_long(x))
elif type(x) is str:
self.encode_tlv(4, x)
elif (type(x) is list) or (type(x) is tuple):
self.encode_tlv(0x30, self.encode_sequence(x))
else:
raise BERException('Unknown type for encoding: %s' % repr(type(x)))
def encode_sequence(data):
b = BER()
for item in data:
b.encode(item)
return str(b)
encode_sequence = staticmethod(encode_sequence)

View File

@ -1,197 +0,0 @@
# Copyright (C) 2006-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
Attempt to generalize the "feeder" part of a Channel: an object which can be
read from and closed, but is reading from a buffer fed by another thread. The
read operations are blocking and can have a timeout set.
"""
import array
import threading
import time
class PipeTimeout (IOError):
"""
Indicates that a timeout was reached on a read from a L{BufferedPipe}.
"""
pass
class BufferedPipe (object):
"""
A buffer that obeys normal read (with timeout) & close semantics for a
file or socket, but is fed data from another thread. This is used by
L{Channel}.
"""
def __init__(self):
self._lock = threading.Lock()
self._cv = threading.Condition(self._lock)
self._event = None
self._buffer = array.array('B')
self._closed = False
def set_event(self, event):
"""
Set an event on this buffer. When data is ready to be read (or the
buffer has been closed), the event will be set. When no data is
ready, the event will be cleared.
@param event: the event to set/clear
@type event: Event
"""
self._event = event
if len(self._buffer) > 0:
event.set()
else:
event.clear()
def feed(self, data):
"""
Feed new data into this pipe. This method is assumed to be called
from a separate thread, so synchronization is done.
@param data: the data to add
@type data: str
"""
self._lock.acquire()
try:
if self._event is not None:
self._event.set()
self._buffer.fromstring(data)
self._cv.notifyAll()
finally:
self._lock.release()
def read_ready(self):
"""
Returns true if data is buffered and ready to be read from this
feeder. A C{False} result does not mean that the feeder has closed;
it means you may need to wait before more data arrives.
@return: C{True} if a L{read} call would immediately return at least
one byte; C{False} otherwise.
@rtype: bool
"""
self._lock.acquire()
try:
if len(self._buffer) == 0:
return False
return True
finally:
self._lock.release()
def read(self, nbytes, timeout=None):
"""
Read data from the pipe. The return value is a string representing
the data received. The maximum amount of data to be received at once
is specified by C{nbytes}. If a string of length zero is returned,
the pipe has been closed.
The optional C{timeout} argument can be a nonnegative float expressing
seconds, or C{None} for no timeout. If a float is given, a
C{PipeTimeout} will be raised if the timeout period value has
elapsed before any data arrives.
@param nbytes: maximum number of bytes to read
@type nbytes: int
@param timeout: maximum seconds to wait (or C{None}, the default, to
wait forever)
@type timeout: float
@return: data
@rtype: str
@raise PipeTimeout: if a timeout was specified and no data was ready
before that timeout
"""
out = ''
self._lock.acquire()
try:
if len(self._buffer) == 0:
if self._closed:
return out
# should we block?
if timeout == 0.0:
raise PipeTimeout()
# loop here in case we get woken up but a different thread has
# grabbed everything in the buffer.
while (len(self._buffer) == 0) and not self._closed:
then = time.time()
self._cv.wait(timeout)
if timeout is not None:
timeout -= time.time() - then
if timeout <= 0.0:
raise PipeTimeout()
# something's in the buffer and we have the lock!
if len(self._buffer) <= nbytes:
out = self._buffer.tostring()
del self._buffer[:]
if (self._event is not None) and not self._closed:
self._event.clear()
else:
out = self._buffer[:nbytes].tostring()
del self._buffer[:nbytes]
finally:
self._lock.release()
return out
def empty(self):
"""
Clear out the buffer and return all data that was in it.
@return: any data that was in the buffer prior to clearing it out
@rtype: str
"""
self._lock.acquire()
try:
out = self._buffer.tostring()
del self._buffer[:]
if (self._event is not None) and not self._closed:
self._event.clear()
return out
finally:
self._lock.release()
def close(self):
"""
Close this pipe object. Future calls to L{read} after the buffer
has been emptied will return immediately with an empty string.
"""
self._lock.acquire()
try:
self._closed = True
self._cv.notifyAll()
if self._event is not None:
self._event.set()
finally:
self._lock.release()
def __len__(self):
"""
Return the number of bytes buffered.
@return: number of bytes bufferes
@rtype: int
"""
self._lock.acquire()
try:
return len(self._buffer)
finally:
self._lock.release()

File diff suppressed because it is too large Load Diff

View File

@ -1,483 +0,0 @@
# Copyright (C) 2006-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
L{SSHClient}.
"""
from binascii import hexlify
import getpass
import os
import socket
import warnings
from paramiko.agent import Agent
from paramiko.common import *
from paramiko.dsskey import DSSKey
from paramiko.hostkeys import HostKeys
from paramiko.resource import ResourceManager
from paramiko.rsakey import RSAKey
from paramiko.ssh_exception import SSHException, BadHostKeyException
from paramiko.transport import Transport
SSH_PORT = 22
class MissingHostKeyPolicy (object):
"""
Interface for defining the policy that L{SSHClient} should use when the
SSH server's hostname is not in either the system host keys or the
application's keys. Pre-made classes implement policies for automatically
adding the key to the application's L{HostKeys} object (L{AutoAddPolicy}),
and for automatically rejecting the key (L{RejectPolicy}).
This function may be used to ask the user to verify the key, for example.
"""
def missing_host_key(self, client, hostname, key):
"""
Called when an L{SSHClient} receives a server key for a server that
isn't in either the system or local L{HostKeys} object. To accept
the key, simply return. To reject, raised an exception (which will
be passed to the calling application).
"""
pass
class AutoAddPolicy (MissingHostKeyPolicy):
"""
Policy for automatically adding the hostname and new host key to the
local L{HostKeys} object, and saving it. This is used by L{SSHClient}.
"""
def missing_host_key(self, client, hostname, key):
client._host_keys.add(hostname, key.get_name(), key)
if client._host_keys_filename is not None:
client.save_host_keys(client._host_keys_filename)
client._log(DEBUG, 'Adding %s host key for %s: %s' %
(key.get_name(), hostname, hexlify(key.get_fingerprint())))
class RejectPolicy (MissingHostKeyPolicy):
"""
Policy for automatically rejecting the unknown hostname & key. This is
used by L{SSHClient}.
"""
def missing_host_key(self, client, hostname, key):
client._log(DEBUG, 'Rejecting %s host key for %s: %s' %
(key.get_name(), hostname, hexlify(key.get_fingerprint())))
raise SSHException('Unknown server %s' % hostname)
class WarningPolicy (MissingHostKeyPolicy):
"""
Policy for logging a python-style warning for an unknown host key, but
accepting it. This is used by L{SSHClient}.
"""
def missing_host_key(self, client, hostname, key):
warnings.warn('Unknown %s host key for %s: %s' %
(key.get_name(), hostname, hexlify(key.get_fingerprint())))
class SSHClient (object):
"""
A high-level representation of a session with an SSH server. This class
wraps L{Transport}, L{Channel}, and L{SFTPClient} to take care of most
aspects of authenticating and opening channels. A typical use case is::
client = SSHClient()
client.load_system_host_keys()
client.connect('ssh.example.com')
stdin, stdout, stderr = client.exec_command('ls -l')
You may pass in explicit overrides for authentication and server host key
checking. The default mechanism is to try to use local key files or an
SSH agent (if one is running).
@since: 1.6
"""
def __init__(self):
"""
Create a new SSHClient.
"""
self._system_host_keys = HostKeys()
self._host_keys = HostKeys()
self._host_keys_filename = None
self._log_channel = None
self._policy = RejectPolicy()
self._transport = None
def load_system_host_keys(self, filename=None):
"""
Load host keys from a system (read-only) file. Host keys read with
this method will not be saved back by L{save_host_keys}.
This method can be called multiple times. Each new set of host keys
will be merged with the existing set (new replacing old if there are
conflicts).
If C{filename} is left as C{None}, an attempt will be made to read
keys from the user's local "known hosts" file, as used by OpenSSH,
and no exception will be raised if the file can't be read. This is
probably only useful on posix.
@param filename: the filename to read, or C{None}
@type filename: str
@raise IOError: if a filename was provided and the file could not be
read
"""
if filename is None:
# try the user's .ssh key file, and mask exceptions
filename = os.path.expanduser('~/.ssh/known_hosts')
try:
self._system_host_keys.load(filename)
except IOError:
pass
return
self._system_host_keys.load(filename)
def load_host_keys(self, filename):
"""
Load host keys from a local host-key file. Host keys read with this
method will be checked I{after} keys loaded via L{load_system_host_keys},
but will be saved back by L{save_host_keys} (so they can be modified).
The missing host key policy L{AutoAddPolicy} adds keys to this set and
saves them, when connecting to a previously-unknown server.
This method can be called multiple times. Each new set of host keys
will be merged with the existing set (new replacing old if there are
conflicts). When automatically saving, the last hostname is used.
@param filename: the filename to read
@type filename: str
@raise IOError: if the filename could not be read
"""
self._host_keys_filename = filename
self._host_keys.load(filename)
def save_host_keys(self, filename):
"""
Save the host keys back to a file. Only the host keys loaded with
L{load_host_keys} (plus any added directly) will be saved -- not any
host keys loaded with L{load_system_host_keys}.
@param filename: the filename to save to
@type filename: str
@raise IOError: if the file could not be written
"""
f = open(filename, 'w')
f.write('# SSH host keys collected by paramiko\n')
for hostname, keys in self._host_keys.iteritems():
for keytype, key in keys.iteritems():
f.write('%s %s %s\n' % (hostname, keytype, key.get_base64()))
f.close()
def get_host_keys(self):
"""
Get the local L{HostKeys} object. This can be used to examine the
local host keys or change them.
@return: the local host keys
@rtype: L{HostKeys}
"""
return self._host_keys
def set_log_channel(self, name):
"""
Set the channel for logging. The default is C{"paramiko.transport"}
but it can be set to anything you want.
@param name: new channel name for logging
@type name: str
"""
self._log_channel = name
def set_missing_host_key_policy(self, policy):
"""
Set the policy to use when connecting to a server that doesn't have a
host key in either the system or local L{HostKeys} objects. The
default policy is to reject all unknown servers (using L{RejectPolicy}).
You may substitute L{AutoAddPolicy} or write your own policy class.
@param policy: the policy to use when receiving a host key from a
previously-unknown server
@type policy: L{MissingHostKeyPolicy}
"""
self._policy = policy
def connect(self, hostname, port=SSH_PORT, username=None, password=None, pkey=None,
key_filename=None, timeout=None, allow_agent=True, look_for_keys=True):
"""
Connect to an SSH server and authenticate to it. The server's host key
is checked against the system host keys (see L{load_system_host_keys})
and any local host keys (L{load_host_keys}). If the server's hostname
is not found in either set of host keys, the missing host key policy
is used (see L{set_missing_host_key_policy}). The default policy is
to reject the key and raise an L{SSHException}.
Authentication is attempted in the following order of priority:
- The C{pkey} or C{key_filename} passed in (if any)
- Any key we can find through an SSH agent
- Any "id_rsa" or "id_dsa" key discoverable in C{~/.ssh/}
- Plain username/password auth, if a password was given
If a private key requires a password to unlock it, and a password is
passed in, that password will be used to attempt to unlock the key.
@param hostname: the server to connect to
@type hostname: str
@param port: the server port to connect to
@type port: int
@param username: the username to authenticate as (defaults to the
current local username)
@type username: str
@param password: a password to use for authentication or for unlocking
a private key
@type password: str
@param pkey: an optional private key to use for authentication
@type pkey: L{PKey}
@param key_filename: the filename, or list of filenames, of optional
private key(s) to try for authentication
@type key_filename: str or list(str)
@param timeout: an optional timeout (in seconds) for the TCP connect
@type timeout: float
@param allow_agent: set to False to disable connecting to the SSH agent
@type allow_agent: bool
@param look_for_keys: set to False to disable searching for discoverable
private key files in C{~/.ssh/}
@type look_for_keys: bool
@raise BadHostKeyException: if the server's host key could not be
verified
@raise AuthenticationException: if authentication failed
@raise SSHException: if there was any other error connecting or
establishing an SSH session
@raise socket.error: if a socket error occurred while connecting
"""
for (family, socktype, proto, canonname, sockaddr) in socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM):
if socktype == socket.SOCK_STREAM:
af = family
addr = sockaddr
break
else:
raise SSHException('No suitable address family for %s' % hostname)
sock = socket.socket(af, socket.SOCK_STREAM)
if timeout is not None:
try:
sock.settimeout(timeout)
except:
pass
sock.connect(addr)
t = self._transport = Transport(sock)
if self._log_channel is not None:
t.set_log_channel(self._log_channel)
t.start_client()
ResourceManager.register(self, t)
server_key = t.get_remote_server_key()
keytype = server_key.get_name()
if port == SSH_PORT:
server_hostkey_name = hostname
else:
server_hostkey_name = "[%s]:%d" % (hostname, port)
our_server_key = self._system_host_keys.get(server_hostkey_name, {}).get(keytype, None)
if our_server_key is None:
our_server_key = self._host_keys.get(server_hostkey_name, {}).get(keytype, None)
if our_server_key is None:
# will raise exception if the key is rejected; let that fall out
self._policy.missing_host_key(self, server_hostkey_name, server_key)
# if the callback returns, assume the key is ok
our_server_key = server_key
if server_key != our_server_key:
raise BadHostKeyException(hostname, server_key, our_server_key)
if username is None:
username = getpass.getuser()
if key_filename is None:
key_filenames = []
elif isinstance(key_filename, (str, unicode)):
key_filenames = [ key_filename ]
else:
key_filenames = key_filename
self._auth(username, password, pkey, key_filenames, allow_agent, look_for_keys)
def close(self):
"""
Close this SSHClient and its underlying L{Transport}.
"""
if self._transport is None:
return
self._transport.close()
self._transport = None
def exec_command(self, command, bufsize=-1):
"""
Execute a command on the SSH server. A new L{Channel} is opened and
the requested command is executed. The command's input and output
streams are returned as python C{file}-like objects representing
stdin, stdout, and stderr.
@param command: the command to execute
@type command: str
@param bufsize: interpreted the same way as by the built-in C{file()} function in python
@type bufsize: int
@return: the stdin, stdout, and stderr of the executing command
@rtype: tuple(L{ChannelFile}, L{ChannelFile}, L{ChannelFile})
@raise SSHException: if the server fails to execute the command
"""
chan = self._transport.open_session()
chan.exec_command(command)
stdin = chan.makefile('wb', bufsize)
stdout = chan.makefile('rb', bufsize)
stderr = chan.makefile_stderr('rb', bufsize)
return stdin, stdout, stderr
def invoke_shell(self, term='vt100', width=80, height=24):
"""
Start an interactive shell session on the SSH server. A new L{Channel}
is opened and connected to a pseudo-terminal using the requested
terminal type and size.
@param term: the terminal type to emulate (for example, C{"vt100"})
@type term: str
@param width: the width (in characters) of the terminal window
@type width: int
@param height: the height (in characters) of the terminal window
@type height: int
@return: a new channel connected to the remote shell
@rtype: L{Channel}
@raise SSHException: if the server fails to invoke a shell
"""
chan = self._transport.open_session()
chan.get_pty(term, width, height)
chan.invoke_shell()
return chan
def open_sftp(self):
"""
Open an SFTP session on the SSH server.
@return: a new SFTP session object
@rtype: L{SFTPClient}
"""
return self._transport.open_sftp_client()
def get_transport(self):
"""
Return the underlying L{Transport} object for this SSH connection.
This can be used to perform lower-level tasks, like opening specific
kinds of channels.
@return: the Transport for this connection
@rtype: L{Transport}
"""
return self._transport
def _auth(self, username, password, pkey, key_filenames, allow_agent, look_for_keys):
"""
Try, in order:
- The key passed in, if one was passed in.
- Any key we can find through an SSH agent (if allowed).
- Any "id_rsa" or "id_dsa" key discoverable in ~/.ssh/ (if allowed).
- Plain username/password auth, if a password was given.
(The password might be needed to unlock a private key.)
"""
saved_exception = None
if pkey is not None:
try:
self._log(DEBUG, 'Trying SSH key %s' % hexlify(pkey.get_fingerprint()))
self._transport.auth_publickey(username, pkey)
return
except SSHException, e:
saved_exception = e
for key_filename in key_filenames:
for pkey_class in (RSAKey, DSSKey):
try:
key = pkey_class.from_private_key_file(key_filename, password)
self._log(DEBUG, 'Trying key %s from %s' % (hexlify(key.get_fingerprint()), key_filename))
self._transport.auth_publickey(username, key)
return
except SSHException, e:
saved_exception = e
if allow_agent:
for key in Agent().get_keys():
try:
self._log(DEBUG, 'Trying SSH agent key %s' % hexlify(key.get_fingerprint()))
self._transport.auth_publickey(username, key)
return
except SSHException, e:
saved_exception = e
keyfiles = []
rsa_key = os.path.expanduser('~/.ssh/id_rsa')
dsa_key = os.path.expanduser('~/.ssh/id_dsa')
if os.path.isfile(rsa_key):
keyfiles.append((RSAKey, rsa_key))
if os.path.isfile(dsa_key):
keyfiles.append((DSSKey, dsa_key))
# look in ~/ssh/ for windows users:
rsa_key = os.path.expanduser('~/ssh/id_rsa')
dsa_key = os.path.expanduser('~/ssh/id_dsa')
if os.path.isfile(rsa_key):
keyfiles.append((RSAKey, rsa_key))
if os.path.isfile(dsa_key):
keyfiles.append((DSSKey, dsa_key))
if not look_for_keys:
keyfiles = []
for pkey_class, filename in keyfiles:
try:
key = pkey_class.from_private_key_file(filename, password)
self._log(DEBUG, 'Trying discovered key %s in %s' % (hexlify(key.get_fingerprint()), filename))
self._transport.auth_publickey(username, key)
return
except SSHException, e:
saved_exception = e
except IOError, e:
saved_exception = e
if password is not None:
try:
self._transport.auth_password(username, password)
return
except SSHException, e:
saved_exception = e
# if we got an auth-failed exception earlier, re-raise it
if saved_exception is not None:
raise saved_exception
raise SSHException('No authentication methods available')
def _log(self, level, msg):
self._transport._log(level, msg)

View File

@ -1,122 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
Common constants and global variables.
"""
MSG_DISCONNECT, MSG_IGNORE, MSG_UNIMPLEMENTED, MSG_DEBUG, MSG_SERVICE_REQUEST, \
MSG_SERVICE_ACCEPT = range(1, 7)
MSG_KEXINIT, MSG_NEWKEYS = range(20, 22)
MSG_USERAUTH_REQUEST, MSG_USERAUTH_FAILURE, MSG_USERAUTH_SUCCESS, \
MSG_USERAUTH_BANNER = range(50, 54)
MSG_USERAUTH_PK_OK = 60
MSG_USERAUTH_INFO_REQUEST, MSG_USERAUTH_INFO_RESPONSE = range(60, 62)
MSG_GLOBAL_REQUEST, MSG_REQUEST_SUCCESS, MSG_REQUEST_FAILURE = range(80, 83)
MSG_CHANNEL_OPEN, MSG_CHANNEL_OPEN_SUCCESS, MSG_CHANNEL_OPEN_FAILURE, \
MSG_CHANNEL_WINDOW_ADJUST, MSG_CHANNEL_DATA, MSG_CHANNEL_EXTENDED_DATA, \
MSG_CHANNEL_EOF, MSG_CHANNEL_CLOSE, MSG_CHANNEL_REQUEST, \
MSG_CHANNEL_SUCCESS, MSG_CHANNEL_FAILURE = range(90, 101)
MSG_NAMES = {
MSG_DISCONNECT: 'disconnect',
MSG_IGNORE: 'ignore',
MSG_UNIMPLEMENTED: 'unimplemented',
MSG_DEBUG: 'debug',
MSG_SERVICE_REQUEST: 'service-request',
MSG_SERVICE_ACCEPT: 'service-accept',
MSG_KEXINIT: 'kexinit',
MSG_NEWKEYS: 'newkeys',
30: 'kex30',
31: 'kex31',
32: 'kex32',
33: 'kex33',
34: 'kex34',
MSG_USERAUTH_REQUEST: 'userauth-request',
MSG_USERAUTH_FAILURE: 'userauth-failure',
MSG_USERAUTH_SUCCESS: 'userauth-success',
MSG_USERAUTH_BANNER: 'userauth--banner',
MSG_USERAUTH_PK_OK: 'userauth-60(pk-ok/info-request)',
MSG_USERAUTH_INFO_RESPONSE: 'userauth-info-response',
MSG_GLOBAL_REQUEST: 'global-request',
MSG_REQUEST_SUCCESS: 'request-success',
MSG_REQUEST_FAILURE: 'request-failure',
MSG_CHANNEL_OPEN: 'channel-open',
MSG_CHANNEL_OPEN_SUCCESS: 'channel-open-success',
MSG_CHANNEL_OPEN_FAILURE: 'channel-open-failure',
MSG_CHANNEL_WINDOW_ADJUST: 'channel-window-adjust',
MSG_CHANNEL_DATA: 'channel-data',
MSG_CHANNEL_EXTENDED_DATA: 'channel-extended-data',
MSG_CHANNEL_EOF: 'channel-eof',
MSG_CHANNEL_CLOSE: 'channel-close',
MSG_CHANNEL_REQUEST: 'channel-request',
MSG_CHANNEL_SUCCESS: 'channel-success',
MSG_CHANNEL_FAILURE: 'channel-failure'
}
# authentication request return codes:
AUTH_SUCCESSFUL, AUTH_PARTIALLY_SUCCESSFUL, AUTH_FAILED = range(3)
# channel request failed reasons:
(OPEN_SUCCEEDED,
OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED,
OPEN_FAILED_CONNECT_FAILED,
OPEN_FAILED_UNKNOWN_CHANNEL_TYPE,
OPEN_FAILED_RESOURCE_SHORTAGE) = range(0, 5)
CONNECTION_FAILED_CODE = {
1: 'Administratively prohibited',
2: 'Connect failed',
3: 'Unknown channel type',
4: 'Resource shortage'
}
DISCONNECT_SERVICE_NOT_AVAILABLE, DISCONNECT_AUTH_CANCELLED_BY_USER, \
DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 7, 13, 14
from rng import StrongLockingRandomPool
# keep a crypto-strong PRNG nearby
randpool = StrongLockingRandomPool()
import sys
if sys.version_info < (2, 3):
try:
import logging
except:
import logging22 as logging
import select
PY22 = True
import socket
if not hasattr(socket, 'timeout'):
class timeout(socket.error): pass
socket.timeout = timeout
del timeout
else:
import logging
PY22 = False
DEBUG = logging.DEBUG
INFO = logging.INFO
WARNING = logging.WARNING
ERROR = logging.ERROR
CRITICAL = logging.CRITICAL

View File

@ -1,36 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
Compression implementations for a Transport.
"""
import zlib
class ZlibCompressor (object):
def __init__(self):
self.z = zlib.compressobj(9)
def __call__(self, data):
return self.z.compress(data) + self.z.flush(zlib.Z_FULL_FLUSH)
class ZlibDecompressor (object):
def __init__(self):
self.z = zlib.decompressobj()
def __call__(self, data):
return self.z.decompress(data)

View File

@ -1,107 +0,0 @@
# Copyright (C) 2006-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
L{SSHConfig}.
"""
import fnmatch
class SSHConfig (object):
"""
Representation of config information as stored in the format used by
OpenSSH. Queries can be made via L{lookup}. The format is described in
OpenSSH's C{ssh_config} man page. This class is provided primarily as a
convenience to posix users (since the OpenSSH format is a de-facto
standard on posix) but should work fine on Windows too.
@since: 1.6
"""
def __init__(self):
"""
Create a new OpenSSH config object.
"""
self._config = [ { 'host': '*' } ]
def parse(self, file_obj):
"""
Read an OpenSSH config from the given file object.
@param file_obj: a file-like object to read the config file from
@type file_obj: file
"""
configs = [self._config[0]]
for line in file_obj:
line = line.rstrip('\n').lstrip()
if (line == '') or (line[0] == '#'):
continue
if '=' in line:
key, value = line.split('=', 1)
key = key.strip().lower()
else:
# find first whitespace, and split there
i = 0
while (i < len(line)) and not line[i].isspace():
i += 1
if i == len(line):
raise Exception('Unparsable line: %r' % line)
key = line[:i].lower()
value = line[i:].lstrip()
if key == 'host':
del configs[:]
# the value may be multiple hosts, space-delimited
for host in value.split():
# do we have a pre-existing host config to append to?
matches = [c for c in self._config if c['host'] == host]
if len(matches) > 0:
configs.append(matches[0])
else:
config = { 'host': host }
self._config.append(config)
configs.append(config)
else:
for config in configs:
config[key] = value
def lookup(self, hostname):
"""
Return a dict of config options for a given hostname.
The host-matching rules of OpenSSH's C{ssh_config} man page are used,
which means that all configuration options from matching host
specifications are merged, with more specific hostmasks taking
precedence. In other words, if C{"Port"} is set under C{"Host *"}
and also C{"Host *.example.com"}, and the lookup is for
C{"ssh.example.com"}, then the port entry for C{"Host *.example.com"}
will win out.
The keys in the returned dict are all normalized to lowercase (look for
C{"port"}, not C{"Port"}. No other processing is done to the keys or
values.
@param hostname: the hostname to lookup
@type hostname: str
"""
matches = [x for x in self._config if fnmatch.fnmatch(hostname, x['host'])]
# sort in order of shortest match (usually '*') to longest
matches.sort(lambda x,y: cmp(len(x['host']), len(y['host'])))
ret = {}
for m in matches:
ret.update(m)
del ret['host']
return ret

View File

@ -1,194 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
L{DSSKey}
"""
from Crypto.PublicKey import DSA
from Crypto.Hash import SHA
from paramiko.common import *
from paramiko import util
from paramiko.ssh_exception import SSHException
from paramiko.message import Message
from paramiko.ber import BER, BERException
from paramiko.pkey import PKey
class DSSKey (PKey):
"""
Representation of a DSS key which can be used to sign an verify SSH2
data.
"""
def __init__(self, msg=None, data=None, filename=None, password=None, vals=None, file_obj=None):
self.p = None
self.q = None
self.g = None
self.y = None
self.x = None
if file_obj is not None:
self._from_private_key(file_obj, password)
return
if filename is not None:
self._from_private_key_file(filename, password)
return
if (msg is None) and (data is not None):
msg = Message(data)
if vals is not None:
self.p, self.q, self.g, self.y = vals
else:
if msg is None:
raise SSHException('Key object may not be empty')
if msg.get_string() != 'ssh-dss':
raise SSHException('Invalid key')
self.p = msg.get_mpint()
self.q = msg.get_mpint()
self.g = msg.get_mpint()
self.y = msg.get_mpint()
self.size = util.bit_length(self.p)
def __str__(self):
m = Message()
m.add_string('ssh-dss')
m.add_mpint(self.p)
m.add_mpint(self.q)
m.add_mpint(self.g)
m.add_mpint(self.y)
return str(m)
def __hash__(self):
h = hash(self.get_name())
h = h * 37 + hash(self.p)
h = h * 37 + hash(self.q)
h = h * 37 + hash(self.g)
h = h * 37 + hash(self.y)
# h might be a long by now...
return hash(h)
def get_name(self):
return 'ssh-dss'
def get_bits(self):
return self.size
def can_sign(self):
return self.x is not None
def sign_ssh_data(self, rpool, data):
digest = SHA.new(data).digest()
dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q), long(self.x)))
# generate a suitable k
qsize = len(util.deflate_long(self.q, 0))
while True:
k = util.inflate_long(rpool.get_bytes(qsize), 1)
if (k > 2) and (k < self.q):
break
r, s = dss.sign(util.inflate_long(digest, 1), k)
m = Message()
m.add_string('ssh-dss')
# apparently, in rare cases, r or s may be shorter than 20 bytes!
rstr = util.deflate_long(r, 0)
sstr = util.deflate_long(s, 0)
if len(rstr) < 20:
rstr = '\x00' * (20 - len(rstr)) + rstr
if len(sstr) < 20:
sstr = '\x00' * (20 - len(sstr)) + sstr
m.add_string(rstr + sstr)
return m
def verify_ssh_sig(self, data, msg):
if len(str(msg)) == 40:
# spies.com bug: signature has no header
sig = str(msg)
else:
kind = msg.get_string()
if kind != 'ssh-dss':
return 0
sig = msg.get_string()
# pull out (r, s) which are NOT encoded as mpints
sigR = util.inflate_long(sig[:20], 1)
sigS = util.inflate_long(sig[20:], 1)
sigM = util.inflate_long(SHA.new(data).digest(), 1)
dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q)))
return dss.verify(sigM, (sigR, sigS))
def _encode_key(self):
if self.x is None:
raise SSHException('Not enough key information')
keylist = [ 0, self.p, self.q, self.g, self.y, self.x ]
try:
b = BER()
b.encode(keylist)
except BERException:
raise SSHException('Unable to create ber encoding of key')
return str(b)
def write_private_key_file(self, filename, password=None):
self._write_private_key_file('DSA', filename, self._encode_key(), password)
def write_private_key(self, file_obj, password=None):
self._write_private_key('DSA', file_obj, self._encode_key(), password)
def generate(bits=1024, progress_func=None):
"""
Generate a new private DSS key. This factory function can be used to
generate a new host key or authentication key.
@param bits: number of bits the generated key should be.
@type bits: int
@param progress_func: an optional function to call at key points in
key generation (used by C{pyCrypto.PublicKey}).
@type progress_func: function
@return: new private key
@rtype: L{DSSKey}
"""
randpool.stir()
dsa = DSA.generate(bits, randpool.get_bytes, progress_func)
key = DSSKey(vals=(dsa.p, dsa.q, dsa.g, dsa.y))
key.x = dsa.x
return key
generate = staticmethod(generate)
### internals...
def _from_private_key_file(self, filename, password):
data = self._read_private_key_file('DSA', filename, password)
self._decode_key(data)
def _from_private_key(self, file_obj, password):
data = self._read_private_key('DSA', file_obj, password)
self._decode_key(data)
def _decode_key(self, data):
# private key file contains:
# DSAPrivateKey = { version = 0, p, q, g, y, x }
try:
keylist = BER(data).decode()
except BERException, x:
raise SSHException('Unable to parse key file: ' + str(x))
if (type(keylist) is not list) or (len(keylist) < 6) or (keylist[0] != 0):
raise SSHException('not a valid DSA private key file (bad ber encoding)')
self.p = keylist[1]
self.q = keylist[2]
self.g = keylist[3]
self.y = keylist[4]
self.x = keylist[5]
self.size = util.bit_length(self.p)

View File

@ -1,453 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
BufferedFile.
"""
from cStringIO import StringIO
class BufferedFile (object):
"""
Reusable base class to implement python-style file buffering around a
simpler stream.
"""
_DEFAULT_BUFSIZE = 8192
SEEK_SET = 0
SEEK_CUR = 1
SEEK_END = 2
FLAG_READ = 0x1
FLAG_WRITE = 0x2
FLAG_APPEND = 0x4
FLAG_BINARY = 0x10
FLAG_BUFFERED = 0x20
FLAG_LINE_BUFFERED = 0x40
FLAG_UNIVERSAL_NEWLINE = 0x80
def __init__(self):
self.newlines = None
self._flags = 0
self._bufsize = self._DEFAULT_BUFSIZE
self._wbuffer = StringIO()
self._rbuffer = ''
self._at_trailing_cr = False
self._closed = False
# pos - position within the file, according to the user
# realpos - position according the OS
# (these may be different because we buffer for line reading)
self._pos = self._realpos = 0
# size only matters for seekable files
self._size = 0
def __del__(self):
self.close()
def __iter__(self):
"""
Returns an iterator that can be used to iterate over the lines in this
file. This iterator happens to return the file itself, since a file is
its own iterator.
@raise ValueError: if the file is closed.
@return: an interator.
@rtype: iterator
"""
if self._closed:
raise ValueError('I/O operation on closed file')
return self
def close(self):
"""
Close the file. Future read and write operations will fail.
"""
self.flush()
self._closed = True
def flush(self):
"""
Write out any data in the write buffer. This may do nothing if write
buffering is not turned on.
"""
self._write_all(self._wbuffer.getvalue())
self._wbuffer = StringIO()
return
def next(self):
"""
Returns the next line from the input, or raises L{StopIteration} when
EOF is hit. Unlike python file objects, it's okay to mix calls to
C{next} and L{readline}.
@raise StopIteration: when the end of the file is reached.
@return: a line read from the file.
@rtype: str
"""
line = self.readline()
if not line:
raise StopIteration
return line
def read(self, size=None):
"""
Read at most C{size} bytes from the file (less if we hit the end of the
file first). If the C{size} argument is negative or omitted, read all
the remaining data in the file.
@param size: maximum number of bytes to read
@type size: int
@return: data read from the file, or an empty string if EOF was
encountered immediately
@rtype: str
"""
if self._closed:
raise IOError('File is closed')
if not (self._flags & self.FLAG_READ):
raise IOError('File is not open for reading')
if (size is None) or (size < 0):
# go for broke
result = self._rbuffer
self._rbuffer = ''
self._pos += len(result)
while True:
try:
new_data = self._read(self._DEFAULT_BUFSIZE)
except EOFError:
new_data = None
if (new_data is None) or (len(new_data) == 0):
break
result += new_data
self._realpos += len(new_data)
self._pos += len(new_data)
return result
if size <= len(self._rbuffer):
result = self._rbuffer[:size]
self._rbuffer = self._rbuffer[size:]
self._pos += len(result)
return result
while len(self._rbuffer) < size:
read_size = size - len(self._rbuffer)
if self._flags & self.FLAG_BUFFERED:
read_size = max(self._bufsize, read_size)
try:
new_data = self._read(read_size)
except EOFError:
new_data = None
if (new_data is None) or (len(new_data) == 0):
break
self._rbuffer += new_data
self._realpos += len(new_data)
result = self._rbuffer[:size]
self._rbuffer = self._rbuffer[size:]
self._pos += len(result)
return result
def readline(self, size=None):
"""
Read one entire line from the file. A trailing newline character is
kept in the string (but may be absent when a file ends with an
incomplete line). If the size argument is present and non-negative, it
is a maximum byte count (including the trailing newline) and an
incomplete line may be returned. An empty string is returned only when
EOF is encountered immediately.
@note: Unlike stdio's C{fgets()}, the returned string contains null
characters (C{'\\0'}) if they occurred in the input.
@param size: maximum length of returned string.
@type size: int
@return: next line of the file, or an empty string if the end of the
file has been reached.
@rtype: str
"""
# it's almost silly how complex this function is.
if self._closed:
raise IOError('File is closed')
if not (self._flags & self.FLAG_READ):
raise IOError('File not open for reading')
line = self._rbuffer
while True:
if self._at_trailing_cr and (self._flags & self.FLAG_UNIVERSAL_NEWLINE) and (len(line) > 0):
# edge case: the newline may be '\r\n' and we may have read
# only the first '\r' last time.
if line[0] == '\n':
line = line[1:]
self._record_newline('\r\n')
else:
self._record_newline('\r')
self._at_trailing_cr = False
# check size before looking for a linefeed, in case we already have
# enough.
if (size is not None) and (size >= 0):
if len(line) >= size:
# truncate line and return
self._rbuffer = line[size:]
line = line[:size]
self._pos += len(line)
return line
n = size - len(line)
else:
n = self._bufsize
if ('\n' in line) or ((self._flags & self.FLAG_UNIVERSAL_NEWLINE) and ('\r' in line)):
break
try:
new_data = self._read(n)
except EOFError:
new_data = None
if (new_data is None) or (len(new_data) == 0):
self._rbuffer = ''
self._pos += len(line)
return line
line += new_data
self._realpos += len(new_data)
# find the newline
pos = line.find('\n')
if self._flags & self.FLAG_UNIVERSAL_NEWLINE:
rpos = line.find('\r')
if (rpos >= 0) and ((rpos < pos) or (pos < 0)):
pos = rpos
xpos = pos + 1
if (line[pos] == '\r') and (xpos < len(line)) and (line[xpos] == '\n'):
xpos += 1
self._rbuffer = line[xpos:]
lf = line[pos:xpos]
line = line[:pos] + '\n'
if (len(self._rbuffer) == 0) and (lf == '\r'):
# we could read the line up to a '\r' and there could still be a
# '\n' following that we read next time. note that and eat it.
self._at_trailing_cr = True
else:
self._record_newline(lf)
self._pos += len(line)
return line
def readlines(self, sizehint=None):
"""
Read all remaining lines using L{readline} and return them as a list.
If the optional C{sizehint} argument is present, instead of reading up
to EOF, whole lines totalling approximately sizehint bytes (possibly
after rounding up to an internal buffer size) are read.
@param sizehint: desired maximum number of bytes to read.
@type sizehint: int
@return: list of lines read from the file.
@rtype: list
"""
lines = []
bytes = 0
while True:
line = self.readline()
if len(line) == 0:
break
lines.append(line)
bytes += len(line)
if (sizehint is not None) and (bytes >= sizehint):
break
return lines
def seek(self, offset, whence=0):
"""
Set the file's current position, like stdio's C{fseek}. Not all file
objects support seeking.
@note: If a file is opened in append mode (C{'a'} or C{'a+'}), any seek
operations will be undone at the next write (as the file position
will move back to the end of the file).
@param offset: position to move to within the file, relative to
C{whence}.
@type offset: int
@param whence: type of movement: 0 = absolute; 1 = relative to the
current position; 2 = relative to the end of the file.
@type whence: int
@raise IOError: if the file doesn't support random access.
"""
raise IOError('File does not support seeking.')
def tell(self):
"""
Return the file's current position. This may not be accurate or
useful if the underlying file doesn't support random access, or was
opened in append mode.
@return: file position (in bytes).
@rtype: int
"""
return self._pos
def write(self, data):
"""
Write data to the file. If write buffering is on (C{bufsize} was
specified and non-zero), some or all of the data may not actually be
written yet. (Use L{flush} or L{close} to force buffered data to be
written out.)
@param data: data to write.
@type data: str
"""
if self._closed:
raise IOError('File is closed')
if not (self._flags & self.FLAG_WRITE):
raise IOError('File not open for writing')
if not (self._flags & self.FLAG_BUFFERED):
self._write_all(data)
return
self._wbuffer.write(data)
if self._flags & self.FLAG_LINE_BUFFERED:
# only scan the new data for linefeed, to avoid wasting time.
last_newline_pos = data.rfind('\n')
if last_newline_pos >= 0:
wbuf = self._wbuffer.getvalue()
last_newline_pos += len(wbuf) - len(data)
self._write_all(wbuf[:last_newline_pos + 1])
self._wbuffer = StringIO()
self._wbuffer.write(wbuf[last_newline_pos + 1:])
return
# even if we're line buffering, if the buffer has grown past the
# buffer size, force a flush.
if self._wbuffer.tell() >= self._bufsize:
self.flush()
return
def writelines(self, sequence):
"""
Write a sequence of strings to the file. The sequence can be any
iterable object producing strings, typically a list of strings. (The
name is intended to match L{readlines}; C{writelines} does not add line
separators.)
@param sequence: an iterable sequence of strings.
@type sequence: sequence
"""
for line in sequence:
self.write(line)
return
def xreadlines(self):
"""
Identical to C{iter(f)}. This is a deprecated file interface that
predates python iterator support.
@return: an iterator.
@rtype: iterator
"""
return self
### overrides...
def _read(self, size):
"""
I{(subclass override)}
Read data from the stream. Return C{None} or raise C{EOFError} to
indicate EOF.
"""
raise EOFError()
def _write(self, data):
"""
I{(subclass override)}
Write data into the stream.
"""
raise IOError('write not implemented')
def _get_size(self):
"""
I{(subclass override)}
Return the size of the file. This is called from within L{_set_mode}
if the file is opened in append mode, so the file position can be
tracked and L{seek} and L{tell} will work correctly. If the file is
a stream that can't be randomly accessed, you don't need to override
this method,
"""
return 0
### internals...
def _set_mode(self, mode='r', bufsize=-1):
"""
Subclasses call this method to initialize the BufferedFile.
"""
# set bufsize in any event, because it's used for readline().
self._bufsize = self._DEFAULT_BUFSIZE
if bufsize < 0:
# do no buffering by default, because otherwise writes will get
# buffered in a way that will probably confuse people.
bufsize = 0
if bufsize == 1:
# apparently, line buffering only affects writes. reads are only
# buffered if you call readline (directly or indirectly: iterating
# over a file will indirectly call readline).
self._flags |= self.FLAG_BUFFERED | self.FLAG_LINE_BUFFERED
elif bufsize > 1:
self._bufsize = bufsize
self._flags |= self.FLAG_BUFFERED
self._flags &= ~self.FLAG_LINE_BUFFERED
elif bufsize == 0:
# unbuffered
self._flags &= ~(self.FLAG_BUFFERED | self.FLAG_LINE_BUFFERED)
if ('r' in mode) or ('+' in mode):
self._flags |= self.FLAG_READ
if ('w' in mode) or ('+' in mode):
self._flags |= self.FLAG_WRITE
if ('a' in mode):
self._flags |= self.FLAG_WRITE | self.FLAG_APPEND
self._size = self._get_size()
self._pos = self._realpos = self._size
if ('b' in mode):
self._flags |= self.FLAG_BINARY
if ('U' in mode):
self._flags |= self.FLAG_UNIVERSAL_NEWLINE
# built-in file objects have this attribute to store which kinds of
# line terminations they've seen:
# <http://www.python.org/doc/current/lib/built-in-funcs.html>
self.newlines = None
def _write_all(self, data):
# the underlying stream may be something that does partial writes (like
# a socket).
while len(data) > 0:
count = self._write(data)
data = data[count:]
if self._flags & self.FLAG_APPEND:
self._size += count
self._pos = self._realpos = self._size
else:
self._pos += count
self._realpos += count
return None
def _record_newline(self, newline):
# silliness about tracking what kinds of newlines we've seen.
# i don't understand why it can be None, a string, or a tuple, instead
# of just always being a tuple, but we'll emulate that behavior anyway.
if not (self._flags & self.FLAG_UNIVERSAL_NEWLINE):
return
if self.newlines is None:
self.newlines = newline
elif (type(self.newlines) is str) and (self.newlines != newline):
self.newlines = (self.newlines, newline)
elif newline not in self.newlines:
self.newlines += (newline,)

View File

@ -1,313 +0,0 @@
# Copyright (C) 2006-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
L{HostKeys}
"""
import base64
from Crypto.Hash import SHA, HMAC
import UserDict
from paramiko.common import *
from paramiko.dsskey import DSSKey
from paramiko.rsakey import RSAKey
class HostKeyEntry:
"""
Representation of a line in an OpenSSH-style "known hosts" file.
"""
def __init__(self, hostnames=None, key=None):
self.valid = (hostnames is not None) and (key is not None)
self.hostnames = hostnames
self.key = key
def from_line(cls, line):
"""
Parses the given line of text to find the names for the host,
the type of key, and the key data. The line is expected to be in the
format used by the openssh known_hosts file.
Lines are expected to not have leading or trailing whitespace.
We don't bother to check for comments or empty lines. All of
that should be taken care of before sending the line to us.
@param line: a line from an OpenSSH known_hosts file
@type line: str
"""
fields = line.split(' ')
if len(fields) < 3:
# Bad number of fields
return None
fields = fields[:3]
names, keytype, key = fields
names = names.split(',')
# Decide what kind of key we're looking at and create an object
# to hold it accordingly.
if keytype == 'ssh-rsa':
key = RSAKey(data=base64.decodestring(key))
elif keytype == 'ssh-dss':
key = DSSKey(data=base64.decodestring(key))
else:
return None
return cls(names, key)
from_line = classmethod(from_line)
def to_line(self):
"""
Returns a string in OpenSSH known_hosts file format, or None if
the object is not in a valid state. A trailing newline is
included.
"""
if self.valid:
return '%s %s %s\n' % (','.join(self.hostnames), self.key.get_name(),
self.key.get_base64())
return None
def __repr__(self):
return '<HostKeyEntry %r: %r>' % (self.hostnames, self.key)
class HostKeys (UserDict.DictMixin):
"""
Representation of an openssh-style "known hosts" file. Host keys can be
read from one or more files, and then individual hosts can be looked up to
verify server keys during SSH negotiation.
A HostKeys object can be treated like a dict; any dict lookup is equivalent
to calling L{lookup}.
@since: 1.5.3
"""
def __init__(self, filename=None):
"""
Create a new HostKeys object, optionally loading keys from an openssh
style host-key file.
@param filename: filename to load host keys from, or C{None}
@type filename: str
"""
# emulate a dict of { hostname: { keytype: PKey } }
self._entries = []
if filename is not None:
self.load(filename)
def add(self, hostname, keytype, key):
"""
Add a host key entry to the table. Any existing entry for a
C{(hostname, keytype)} pair will be replaced.
@param hostname: the hostname (or IP) to add
@type hostname: str
@param keytype: key type (C{"ssh-rsa"} or C{"ssh-dss"})
@type keytype: str
@param key: the key to add
@type key: L{PKey}
"""
for e in self._entries:
if (hostname in e.hostnames) and (e.key.get_name() == keytype):
e.key = key
return
self._entries.append(HostKeyEntry([hostname], key))
def load(self, filename):
"""
Read a file of known SSH host keys, in the format used by openssh.
This type of file unfortunately doesn't exist on Windows, but on
posix, it will usually be stored in
C{os.path.expanduser("~/.ssh/known_hosts")}.
If this method is called multiple times, the host keys are merged,
not cleared. So multiple calls to C{load} will just call L{add},
replacing any existing entries and adding new ones.
@param filename: name of the file to read host keys from
@type filename: str
@raise IOError: if there was an error reading the file
"""
f = open(filename, 'r')
for line in f:
line = line.strip()
if (len(line) == 0) or (line[0] == '#'):
continue
e = HostKeyEntry.from_line(line)
if e is not None:
self._entries.append(e)
f.close()
def save(self, filename):
"""
Save host keys into a file, in the format used by openssh. The order of
keys in the file will be preserved when possible (if these keys were
loaded from a file originally). The single exception is that combined
lines will be split into individual key lines, which is arguably a bug.
@param filename: name of the file to write
@type filename: str
@raise IOError: if there was an error writing the file
@since: 1.6.1
"""
f = open(filename, 'w')
for e in self._entries:
line = e.to_line()
if line:
f.write(line)
f.close()
def lookup(self, hostname):
"""
Find a hostkey entry for a given hostname or IP. If no entry is found,
C{None} is returned. Otherwise a dictionary of keytype to key is
returned. The keytype will be either C{"ssh-rsa"} or C{"ssh-dss"}.
@param hostname: the hostname (or IP) to lookup
@type hostname: str
@return: keys associated with this host (or C{None})
@rtype: dict(str, L{PKey})
"""
class SubDict (UserDict.DictMixin):
def __init__(self, hostname, entries, hostkeys):
self._hostname = hostname
self._entries = entries
self._hostkeys = hostkeys
def __getitem__(self, key):
for e in self._entries:
if e.key.get_name() == key:
return e.key
raise KeyError(key)
def __setitem__(self, key, val):
for e in self._entries:
if e.key is None:
continue
if e.key.get_name() == key:
# replace
e.key = val
break
else:
# add a new one
e = HostKeyEntry([hostname], val)
self._entries.append(e)
self._hostkeys._entries.append(e)
def keys(self):
return [e.key.get_name() for e in self._entries if e.key is not None]
entries = []
for e in self._entries:
for h in e.hostnames:
if (h.startswith('|1|') and (self.hash_host(hostname, h) == h)) or (h == hostname):
entries.append(e)
if len(entries) == 0:
return None
return SubDict(hostname, entries, self)
def check(self, hostname, key):
"""
Return True if the given key is associated with the given hostname
in this dictionary.
@param hostname: hostname (or IP) of the SSH server
@type hostname: str
@param key: the key to check
@type key: L{PKey}
@return: C{True} if the key is associated with the hostname; C{False}
if not
@rtype: bool
"""
k = self.lookup(hostname)
if k is None:
return False
host_key = k.get(key.get_name(), None)
if host_key is None:
return False
return str(host_key) == str(key)
def clear(self):
"""
Remove all host keys from the dictionary.
"""
self._entries = []
def __getitem__(self, key):
ret = self.lookup(key)
if ret is None:
raise KeyError(key)
return ret
def __setitem__(self, hostname, entry):
# don't use this please.
if len(entry) == 0:
self._entries.append(HostKeyEntry([hostname], None))
return
for key_type in entry.keys():
found = False
for e in self._entries:
if (hostname in e.hostnames) and (e.key.get_name() == key_type):
# replace
e.key = entry[key_type]
found = True
if not found:
self._entries.append(HostKeyEntry([hostname], entry[key_type]))
def keys(self):
# python 2.4 sets would be nice here.
ret = []
for e in self._entries:
for h in e.hostnames:
if h not in ret:
ret.append(h)
return ret
def values(self):
ret = []
for k in self.keys():
ret.append(self.lookup(k))
return ret
def hash_host(hostname, salt=None):
"""
Return a "hashed" form of the hostname, as used by openssh when storing
hashed hostnames in the known_hosts file.
@param hostname: the hostname to hash
@type hostname: str
@param salt: optional salt to use when hashing (must be 20 bytes long)
@type salt: str
@return: the hashed hostname
@rtype: str
"""
if salt is None:
salt = randpool.get_bytes(SHA.digest_size)
else:
if salt.startswith('|1|'):
salt = salt.split('|')[2]
salt = base64.decodestring(salt)
assert len(salt) == SHA.digest_size
hmac = HMAC.HMAC(salt, hostname, SHA).digest()
hostkey = '|1|%s|%s' % (base64.encodestring(salt), base64.encodestring(hmac))
return hostkey.replace('\n', '')
hash_host = staticmethod(hash_host)

View File

@ -1,241 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
Variant on L{KexGroup1 <paramiko.kex_group1.KexGroup1>} where the prime "p" and
generator "g" are provided by the server. A bit more work is required on the
client side, and a B{lot} more on the server side.
"""
from Crypto.Hash import SHA
from Crypto.Util import number
from paramiko.common import *
from paramiko import util
from paramiko.message import Message
from paramiko.ssh_exception import SSHException
_MSG_KEXDH_GEX_REQUEST_OLD, _MSG_KEXDH_GEX_GROUP, _MSG_KEXDH_GEX_INIT, \
_MSG_KEXDH_GEX_REPLY, _MSG_KEXDH_GEX_REQUEST = range(30, 35)
class KexGex (object):
name = 'diffie-hellman-group-exchange-sha1'
min_bits = 1024
max_bits = 8192
preferred_bits = 2048
def __init__(self, transport):
self.transport = transport
self.p = None
self.q = None
self.g = None
self.x = None
self.e = None
self.f = None
self.old_style = False
def start_kex(self, _test_old_style=False):
if self.transport.server_mode:
self.transport._expect_packet(_MSG_KEXDH_GEX_REQUEST, _MSG_KEXDH_GEX_REQUEST_OLD)
return
# request a bit range: we accept (min_bits) to (max_bits), but prefer
# (preferred_bits). according to the spec, we shouldn't pull the
# minimum up above 1024.
m = Message()
if _test_old_style:
# only used for unit tests: we shouldn't ever send this
m.add_byte(chr(_MSG_KEXDH_GEX_REQUEST_OLD))
m.add_int(self.preferred_bits)
self.old_style = True
else:
m.add_byte(chr(_MSG_KEXDH_GEX_REQUEST))
m.add_int(self.min_bits)
m.add_int(self.preferred_bits)
m.add_int(self.max_bits)
self.transport._send_message(m)
self.transport._expect_packet(_MSG_KEXDH_GEX_GROUP)
def parse_next(self, ptype, m):
if ptype == _MSG_KEXDH_GEX_REQUEST:
return self._parse_kexdh_gex_request(m)
elif ptype == _MSG_KEXDH_GEX_GROUP:
return self._parse_kexdh_gex_group(m)
elif ptype == _MSG_KEXDH_GEX_INIT:
return self._parse_kexdh_gex_init(m)
elif ptype == _MSG_KEXDH_GEX_REPLY:
return self._parse_kexdh_gex_reply(m)
elif ptype == _MSG_KEXDH_GEX_REQUEST_OLD:
return self._parse_kexdh_gex_request_old(m)
raise SSHException('KexGex asked to handle packet type %d' % ptype)
### internals...
def _generate_x(self):
# generate an "x" (1 < x < (p-1)/2).
q = (self.p - 1) // 2
qnorm = util.deflate_long(q, 0)
qhbyte = ord(qnorm[0])
bytes = len(qnorm)
qmask = 0xff
while not (qhbyte & 0x80):
qhbyte <<= 1
qmask >>= 1
while True:
self.transport.randpool.stir()
x_bytes = self.transport.randpool.get_bytes(bytes)
x_bytes = chr(ord(x_bytes[0]) & qmask) + x_bytes[1:]
x = util.inflate_long(x_bytes, 1)
if (x > 1) and (x < q):
break
self.x = x
def _parse_kexdh_gex_request(self, m):
minbits = m.get_int()
preferredbits = m.get_int()
maxbits = m.get_int()
# smoosh the user's preferred size into our own limits
if preferredbits > self.max_bits:
preferredbits = self.max_bits
if preferredbits < self.min_bits:
preferredbits = self.min_bits
# fix min/max if they're inconsistent. technically, we could just pout
# and hang up, but there's no harm in giving them the benefit of the
# doubt and just picking a bitsize for them.
if minbits > preferredbits:
minbits = preferredbits
if maxbits < preferredbits:
maxbits = preferredbits
# now save a copy
self.min_bits = minbits
self.preferred_bits = preferredbits
self.max_bits = maxbits
# generate prime
pack = self.transport._get_modulus_pack()
if pack is None:
raise SSHException('Can\'t do server-side gex with no modulus pack')
self.transport._log(DEBUG, 'Picking p (%d <= %d <= %d bits)' % (minbits, preferredbits, maxbits))
self.g, self.p = pack.get_modulus(minbits, preferredbits, maxbits)
m = Message()
m.add_byte(chr(_MSG_KEXDH_GEX_GROUP))
m.add_mpint(self.p)
m.add_mpint(self.g)
self.transport._send_message(m)
self.transport._expect_packet(_MSG_KEXDH_GEX_INIT)
def _parse_kexdh_gex_request_old(self, m):
# same as above, but without min_bits or max_bits (used by older clients like putty)
self.preferred_bits = m.get_int()
# smoosh the user's preferred size into our own limits
if self.preferred_bits > self.max_bits:
self.preferred_bits = self.max_bits
if self.preferred_bits < self.min_bits:
self.preferred_bits = self.min_bits
# generate prime
pack = self.transport._get_modulus_pack()
if pack is None:
raise SSHException('Can\'t do server-side gex with no modulus pack')
self.transport._log(DEBUG, 'Picking p (~ %d bits)' % (self.preferred_bits,))
self.g, self.p = pack.get_modulus(self.min_bits, self.preferred_bits, self.max_bits)
m = Message()
m.add_byte(chr(_MSG_KEXDH_GEX_GROUP))
m.add_mpint(self.p)
m.add_mpint(self.g)
self.transport._send_message(m)
self.transport._expect_packet(_MSG_KEXDH_GEX_INIT)
self.old_style = True
def _parse_kexdh_gex_group(self, m):
self.p = m.get_mpint()
self.g = m.get_mpint()
# reject if p's bit length < 1024 or > 8192
bitlen = util.bit_length(self.p)
if (bitlen < 1024) or (bitlen > 8192):
raise SSHException('Server-generated gex p (don\'t ask) is out of range (%d bits)' % bitlen)
self.transport._log(DEBUG, 'Got server p (%d bits)' % bitlen)
self._generate_x()
# now compute e = g^x mod p
self.e = pow(self.g, self.x, self.p)
m = Message()
m.add_byte(chr(_MSG_KEXDH_GEX_INIT))
m.add_mpint(self.e)
self.transport._send_message(m)
self.transport._expect_packet(_MSG_KEXDH_GEX_REPLY)
def _parse_kexdh_gex_init(self, m):
self.e = m.get_mpint()
if (self.e < 1) or (self.e > self.p - 1):
raise SSHException('Client kex "e" is out of range')
self._generate_x()
self.f = pow(self.g, self.x, self.p)
K = pow(self.e, self.x, self.p)
key = str(self.transport.get_server_key())
# okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K)
hm = Message()
hm.add(self.transport.remote_version, self.transport.local_version,
self.transport.remote_kex_init, self.transport.local_kex_init,
key)
if not self.old_style:
hm.add_int(self.min_bits)
hm.add_int(self.preferred_bits)
if not self.old_style:
hm.add_int(self.max_bits)
hm.add_mpint(self.p)
hm.add_mpint(self.g)
hm.add_mpint(self.e)
hm.add_mpint(self.f)
hm.add_mpint(K)
H = SHA.new(str(hm)).digest()
self.transport._set_K_H(K, H)
# sign it
sig = self.transport.get_server_key().sign_ssh_data(self.transport.randpool, H)
# send reply
m = Message()
m.add_byte(chr(_MSG_KEXDH_GEX_REPLY))
m.add_string(key)
m.add_mpint(self.f)
m.add_string(str(sig))
self.transport._send_message(m)
self.transport._activate_outbound()
def _parse_kexdh_gex_reply(self, m):
host_key = m.get_string()
self.f = m.get_mpint()
sig = m.get_string()
if (self.f < 1) or (self.f > self.p - 1):
raise SSHException('Server kex "f" is out of range')
K = pow(self.f, self.x, self.p)
# okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K)
hm = Message()
hm.add(self.transport.local_version, self.transport.remote_version,
self.transport.local_kex_init, self.transport.remote_kex_init,
host_key)
if not self.old_style:
hm.add_int(self.min_bits)
hm.add_int(self.preferred_bits)
if not self.old_style:
hm.add_int(self.max_bits)
hm.add_mpint(self.p)
hm.add_mpint(self.g)
hm.add_mpint(self.e)
hm.add_mpint(self.f)
hm.add_mpint(K)
self.transport._set_K_H(K, SHA.new(str(hm)).digest())
self.transport._verify_key(host_key, sig)
self.transport._activate_outbound()

View File

@ -1,133 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
Standard SSH key exchange ("kex" if you wanna sound cool). Diffie-Hellman of
1024 bit key halves, using a known "p" prime and "g" generator.
"""
from Crypto.Hash import SHA
from paramiko.common import *
from paramiko import util
from paramiko.message import Message
from paramiko.ssh_exception import SSHException
_MSG_KEXDH_INIT, _MSG_KEXDH_REPLY = range(30, 32)
# draft-ietf-secsh-transport-09.txt, page 17
P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFFL
G = 2
class KexGroup1(object):
name = 'diffie-hellman-group1-sha1'
def __init__(self, transport):
self.transport = transport
self.x = 0L
self.e = 0L
self.f = 0L
def start_kex(self):
self._generate_x()
if self.transport.server_mode:
# compute f = g^x mod p, but don't send it yet
self.f = pow(G, self.x, P)
self.transport._expect_packet(_MSG_KEXDH_INIT)
return
# compute e = g^x mod p (where g=2), and send it
self.e = pow(G, self.x, P)
m = Message()
m.add_byte(chr(_MSG_KEXDH_INIT))
m.add_mpint(self.e)
self.transport._send_message(m)
self.transport._expect_packet(_MSG_KEXDH_REPLY)
def parse_next(self, ptype, m):
if self.transport.server_mode and (ptype == _MSG_KEXDH_INIT):
return self._parse_kexdh_init(m)
elif not self.transport.server_mode and (ptype == _MSG_KEXDH_REPLY):
return self._parse_kexdh_reply(m)
raise SSHException('KexGroup1 asked to handle packet type %d' % ptype)
### internals...
def _generate_x(self):
# generate an "x" (1 < x < q), where q is (p-1)/2.
# p is a 128-byte (1024-bit) number, where the first 64 bits are 1.
# therefore q can be approximated as a 2^1023. we drop the subset of
# potential x where the first 63 bits are 1, because some of those will be
# larger than q (but this is a tiny tiny subset of potential x).
while 1:
self.transport.randpool.stir()
x_bytes = self.transport.randpool.get_bytes(128)
x_bytes = chr(ord(x_bytes[0]) & 0x7f) + x_bytes[1:]
if (x_bytes[:8] != '\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF') and \
(x_bytes[:8] != '\x00\x00\x00\x00\x00\x00\x00\x00'):
break
self.x = util.inflate_long(x_bytes)
def _parse_kexdh_reply(self, m):
# client mode
host_key = m.get_string()
self.f = m.get_mpint()
if (self.f < 1) or (self.f > P - 1):
raise SSHException('Server kex "f" is out of range')
sig = m.get_string()
K = pow(self.f, self.x, P)
# okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || e || f || K)
hm = Message()
hm.add(self.transport.local_version, self.transport.remote_version,
self.transport.local_kex_init, self.transport.remote_kex_init)
hm.add_string(host_key)
hm.add_mpint(self.e)
hm.add_mpint(self.f)
hm.add_mpint(K)
self.transport._set_K_H(K, SHA.new(str(hm)).digest())
self.transport._verify_key(host_key, sig)
self.transport._activate_outbound()
def _parse_kexdh_init(self, m):
# server mode
self.e = m.get_mpint()
if (self.e < 1) or (self.e > P - 1):
raise SSHException('Client kex "e" is out of range')
K = pow(self.e, self.x, P)
key = str(self.transport.get_server_key())
# okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || e || f || K)
hm = Message()
hm.add(self.transport.remote_version, self.transport.local_version,
self.transport.remote_kex_init, self.transport.local_kex_init)
hm.add_string(key)
hm.add_mpint(self.e)
hm.add_mpint(self.f)
hm.add_mpint(K)
H = SHA.new(str(hm)).digest()
self.transport._set_K_H(K, H)
# sign it
sig = self.transport.get_server_key().sign_ssh_data(self.transport.randpool, H)
# send reply
m = Message()
m.add_byte(chr(_MSG_KEXDH_REPLY))
m.add_string(key)
m.add_mpint(self.f)
m.add_string(str(sig))
self.transport._send_message(m)
self.transport._activate_outbound()

View File

@ -1,63 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
Stub out logging on python < 2.3.
"""
DEBUG = 10
INFO = 20
WARNING = 30
ERROR = 40
CRITICAL = 50
def getLogger(name):
return _logger
class logger (object):
def __init__(self):
self.handlers = [ ]
self.level = ERROR
def setLevel(self, level):
self.level = level
def addHandler(self, h):
self.handlers.append(h)
def addFilter(self, filter):
pass
def log(self, level, text):
if level >= self.level:
for h in self.handlers:
h.f.write(text + '\n')
h.f.flush()
class StreamHandler (object):
def __init__(self, f):
self.f = f
def setFormatter(self, f):
pass
class Formatter (object):
def __init__(self, x, y):
pass
_logger = logger()

View File

@ -1,298 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
Implementation of an SSH2 "message".
"""
import struct
import cStringIO
from paramiko import util
class Message (object):
"""
An SSH2 I{Message} is a stream of bytes that encodes some combination of
strings, integers, bools, and infinite-precision integers (known in python
as I{long}s). This class builds or breaks down such a byte stream.
Normally you don't need to deal with anything this low-level, but it's
exposed for people implementing custom extensions, or features that
paramiko doesn't support yet.
"""
def __init__(self, content=None):
"""
Create a new SSH2 Message.
@param content: the byte stream to use as the Message content (passed
in only when decomposing a Message).
@type content: string
"""
if content != None:
self.packet = cStringIO.StringIO(content)
else:
self.packet = cStringIO.StringIO()
def __str__(self):
"""
Return the byte stream content of this Message, as a string.
@return: the contents of this Message.
@rtype: string
"""
return self.packet.getvalue()
def __repr__(self):
"""
Returns a string representation of this object, for debugging.
@rtype: string
"""
return 'paramiko.Message(' + repr(self.packet.getvalue()) + ')'
def rewind(self):
"""
Rewind the message to the beginning as if no items had been parsed
out of it yet.
"""
self.packet.seek(0)
def get_remainder(self):
"""
Return the bytes of this Message that haven't already been parsed and
returned.
@return: a string of the bytes not parsed yet.
@rtype: string
"""
position = self.packet.tell()
remainder = self.packet.read()
self.packet.seek(position)
return remainder
def get_so_far(self):
"""
Returns the bytes of this Message that have been parsed and returned.
The string passed into a Message's constructor can be regenerated by
concatenating C{get_so_far} and L{get_remainder}.
@return: a string of the bytes parsed so far.
@rtype: string
"""
position = self.packet.tell()
self.rewind()
return self.packet.read(position)
def get_bytes(self, n):
"""
Return the next C{n} bytes of the Message, without decomposing into
an int, string, etc. Just the raw bytes are returned.
@return: a string of the next C{n} bytes of the Message, or a string
of C{n} zero bytes, if there aren't C{n} bytes remaining.
@rtype: string
"""
b = self.packet.read(n)
if len(b) < n:
return b + '\x00' * (n - len(b))
return b
def get_byte(self):
"""
Return the next byte of the Message, without decomposing it. This
is equivalent to L{get_bytes(1)<get_bytes>}.
@return: the next byte of the Message, or C{'\000'} if there aren't
any bytes remaining.
@rtype: string
"""
return self.get_bytes(1)
def get_boolean(self):
"""
Fetch a boolean from the stream.
@return: C{True} or C{False} (from the Message).
@rtype: bool
"""
b = self.get_bytes(1)
return b != '\x00'
def get_int(self):
"""
Fetch an int from the stream.
@return: a 32-bit unsigned integer.
@rtype: int
"""
return struct.unpack('>I', self.get_bytes(4))[0]
def get_int64(self):
"""
Fetch a 64-bit int from the stream.
@return: a 64-bit unsigned integer.
@rtype: long
"""
return struct.unpack('>Q', self.get_bytes(8))[0]
def get_mpint(self):
"""
Fetch a long int (mpint) from the stream.
@return: an arbitrary-length integer.
@rtype: long
"""
return util.inflate_long(self.get_string())
def get_string(self):
"""
Fetch a string from the stream. This could be a byte string and may
contain unprintable characters. (It's not unheard of for a string to
contain another byte-stream Message.)
@return: a string.
@rtype: string
"""
return self.get_bytes(self.get_int())
def get_list(self):
"""
Fetch a list of strings from the stream. These are trivially encoded
as comma-separated values in a string.
@return: a list of strings.
@rtype: list of strings
"""
return self.get_string().split(',')
def add_bytes(self, b):
"""
Write bytes to the stream, without any formatting.
@param b: bytes to add
@type b: str
"""
self.packet.write(b)
return self
def add_byte(self, b):
"""
Write a single byte to the stream, without any formatting.
@param b: byte to add
@type b: str
"""
self.packet.write(b)
return self
def add_boolean(self, b):
"""
Add a boolean value to the stream.
@param b: boolean value to add
@type b: bool
"""
if b:
self.add_byte('\x01')
else:
self.add_byte('\x00')
return self
def add_int(self, n):
"""
Add an integer to the stream.
@param n: integer to add
@type n: int
"""
self.packet.write(struct.pack('>I', n))
return self
def add_int64(self, n):
"""
Add a 64-bit int to the stream.
@param n: long int to add
@type n: long
"""
self.packet.write(struct.pack('>Q', n))
return self
def add_mpint(self, z):
"""
Add a long int to the stream, encoded as an infinite-precision
integer. This method only works on positive numbers.
@param z: long int to add
@type z: long
"""
self.add_string(util.deflate_long(z))
return self
def add_string(self, s):
"""
Add a string to the stream.
@param s: string to add
@type s: str
"""
self.add_int(len(s))
self.packet.write(s)
return self
def add_list(self, l):
"""
Add a list of strings to the stream. They are encoded identically to
a single string of values separated by commas. (Yes, really, that's
how SSH2 does it.)
@param l: list of strings to add
@type l: list(str)
"""
self.add_string(','.join(l))
return self
def _add(self, i):
if type(i) is str:
return self.add_string(i)
elif type(i) is int:
return self.add_int(i)
elif type(i) is long:
if i > 0xffffffffL:
return self.add_mpint(i)
else:
return self.add_int(i)
elif type(i) is bool:
return self.add_boolean(i)
elif type(i) is list:
return self.add_list(i)
else:
raise Exception('Unknown type')
def add(self, *seq):
"""
Add a sequence of items to the stream. The values are encoded based
on their type: str, int, bool, list, or long.
@param seq: the sequence of items
@type seq: sequence
@bug: longs are encoded non-deterministically. Don't use this method.
"""
for item in seq:
self._add(item)

View File

@ -1,485 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
Packetizer.
"""
import errno
import select
import socket
import struct
import threading
import time
from paramiko.common import *
from paramiko import util
from paramiko.ssh_exception import SSHException
from paramiko.message import Message
got_r_hmac = False
try:
import r_hmac
got_r_hmac = True
except ImportError:
pass
def compute_hmac(key, message, digest_class):
if got_r_hmac:
return r_hmac.HMAC(key, message, digest_class).digest()
from Crypto.Hash import HMAC
return HMAC.HMAC(key, message, digest_class).digest()
class NeedRekeyException (Exception):
pass
class Packetizer (object):
"""
Implementation of the base SSH packet protocol.
"""
# READ the secsh RFC's before raising these values. if anything,
# they should probably be lower.
REKEY_PACKETS = pow(2, 30)
REKEY_BYTES = pow(2, 30)
def __init__(self, socket):
self.__socket = socket
self.__logger = None
self.__closed = False
self.__dump_packets = False
self.__need_rekey = False
self.__init_count = 0
self.__remainder = ''
# used for noticing when to re-key:
self.__sent_bytes = 0
self.__sent_packets = 0
self.__received_bytes = 0
self.__received_packets = 0
self.__received_packets_overflow = 0
# current inbound/outbound ciphering:
self.__block_size_out = 8
self.__block_size_in = 8
self.__mac_size_out = 0
self.__mac_size_in = 0
self.__block_engine_out = None
self.__block_engine_in = None
self.__mac_engine_out = None
self.__mac_engine_in = None
self.__mac_key_out = ''
self.__mac_key_in = ''
self.__compress_engine_out = None
self.__compress_engine_in = None
self.__sequence_number_out = 0L
self.__sequence_number_in = 0L
# lock around outbound writes (packet computation)
self.__write_lock = threading.RLock()
# keepalives:
self.__keepalive_interval = 0
self.__keepalive_last = time.time()
self.__keepalive_callback = None
def set_log(self, log):
"""
Set the python log object to use for logging.
"""
self.__logger = log
def set_outbound_cipher(self, block_engine, block_size, mac_engine, mac_size, mac_key):
"""
Switch outbound data cipher.
"""
self.__block_engine_out = block_engine
self.__block_size_out = block_size
self.__mac_engine_out = mac_engine
self.__mac_size_out = mac_size
self.__mac_key_out = mac_key
self.__sent_bytes = 0
self.__sent_packets = 0
# wait until the reset happens in both directions before clearing rekey flag
self.__init_count |= 1
if self.__init_count == 3:
self.__init_count = 0
self.__need_rekey = False
def set_inbound_cipher(self, block_engine, block_size, mac_engine, mac_size, mac_key):
"""
Switch inbound data cipher.
"""
self.__block_engine_in = block_engine
self.__block_size_in = block_size
self.__mac_engine_in = mac_engine
self.__mac_size_in = mac_size
self.__mac_key_in = mac_key
self.__received_bytes = 0
self.__received_packets = 0
self.__received_packets_overflow = 0
# wait until the reset happens in both directions before clearing rekey flag
self.__init_count |= 2
if self.__init_count == 3:
self.__init_count = 0
self.__need_rekey = False
def set_outbound_compressor(self, compressor):
self.__compress_engine_out = compressor
def set_inbound_compressor(self, compressor):
self.__compress_engine_in = compressor
def close(self):
self.__closed = True
self.__socket.close()
def set_hexdump(self, hexdump):
self.__dump_packets = hexdump
def get_hexdump(self):
return self.__dump_packets
def get_mac_size_in(self):
return self.__mac_size_in
def get_mac_size_out(self):
return self.__mac_size_out
def need_rekey(self):
"""
Returns C{True} if a new set of keys needs to be negotiated. This
will be triggered during a packet read or write, so it should be
checked after every read or write, or at least after every few.
@return: C{True} if a new set of keys needs to be negotiated
"""
return self.__need_rekey
def set_keepalive(self, interval, callback):
"""
Turn on/off the callback keepalive. If C{interval} seconds pass with
no data read from or written to the socket, the callback will be
executed and the timer will be reset.
"""
self.__keepalive_interval = interval
self.__keepalive_callback = callback
self.__keepalive_last = time.time()
def read_all(self, n, check_rekey=False):
"""
Read as close to N bytes as possible, blocking as long as necessary.
@param n: number of bytes to read
@type n: int
@return: the data read
@rtype: str
@raise EOFError: if the socket was closed before all the bytes could
be read
"""
out = ''
# handle over-reading from reading the banner line
if len(self.__remainder) > 0:
out = self.__remainder[:n]
self.__remainder = self.__remainder[n:]
n -= len(out)
if PY22:
return self._py22_read_all(n, out)
while n > 0:
got_timeout = False
try:
x = self.__socket.recv(n)
if len(x) == 0:
raise EOFError()
out += x
n -= len(x)
except socket.timeout:
got_timeout = True
except socket.error, e:
# on Linux, sometimes instead of socket.timeout, we get
# EAGAIN. this is a bug in recent (> 2.6.9) kernels but
# we need to work around it.
if (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EAGAIN):
got_timeout = True
elif (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EINTR):
# syscall interrupted; try again
pass
elif self.__closed:
raise EOFError()
else:
raise
if got_timeout:
if self.__closed:
raise EOFError()
if check_rekey and (len(out) == 0) and self.__need_rekey:
raise NeedRekeyException()
self._check_keepalive()
return out
def write_all(self, out):
self.__keepalive_last = time.time()
while len(out) > 0:
got_timeout = False
try:
n = self.__socket.send(out)
except socket.timeout:
got_timeout = True
except socket.error, e:
if (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EAGAIN):
got_timeout = True
elif (type(e.args) is tuple) and (len(e.args) > 0) and (e.args[0] == errno.EINTR):
# syscall interrupted; try again
pass
else:
n = -1
except Exception:
# could be: (32, 'Broken pipe')
n = -1
if got_timeout:
n = 0
if self.__closed:
n = -1
if n < 0:
raise EOFError()
if n == len(out):
break
out = out[n:]
return
def readline(self, timeout):
"""
Read a line from the socket. We assume no data is pending after the
line, so it's okay to attempt large reads.
"""
buf = self.__remainder
while not '\n' in buf:
buf += self._read_timeout(timeout)
n = buf.index('\n')
self.__remainder = buf[n+1:]
buf = buf[:n]
if (len(buf) > 0) and (buf[-1] == '\r'):
buf = buf[:-1]
return buf
def send_message(self, data):
"""
Write a block of data using the current cipher, as an SSH block.
"""
# encrypt this sucka
data = str(data)
cmd = ord(data[0])
if cmd in MSG_NAMES:
cmd_name = MSG_NAMES[cmd]
else:
cmd_name = '$%x' % cmd
orig_len = len(data)
self.__write_lock.acquire()
try:
if self.__compress_engine_out is not None:
data = self.__compress_engine_out(data)
packet = self._build_packet(data)
if self.__dump_packets:
self._log(DEBUG, 'Write packet <%s>, length %d' % (cmd_name, orig_len))
self._log(DEBUG, util.format_binary(packet, 'OUT: '))
if self.__block_engine_out != None:
out = self.__block_engine_out.encrypt(packet)
else:
out = packet
# + mac
if self.__block_engine_out != None:
payload = struct.pack('>I', self.__sequence_number_out) + packet
out += compute_hmac(self.__mac_key_out, payload, self.__mac_engine_out)[:self.__mac_size_out]
self.__sequence_number_out = (self.__sequence_number_out + 1) & 0xffffffffL
self.write_all(out)
self.__sent_bytes += len(out)
self.__sent_packets += 1
if (self.__sent_packets % 100) == 0:
# stirring the randpool takes 30ms on my ibook!!
randpool.stir()
if ((self.__sent_packets >= self.REKEY_PACKETS) or (self.__sent_bytes >= self.REKEY_BYTES)) \
and not self.__need_rekey:
# only ask once for rekeying
self._log(DEBUG, 'Rekeying (hit %d packets, %d bytes sent)' %
(self.__sent_packets, self.__sent_bytes))
self.__received_packets_overflow = 0
self._trigger_rekey()
finally:
self.__write_lock.release()
def read_message(self):
"""
Only one thread should ever be in this function (no other locking is
done).
@raise SSHException: if the packet is mangled
@raise NeedRekeyException: if the transport should rekey
"""
header = self.read_all(self.__block_size_in, check_rekey=True)
if self.__block_engine_in != None:
header = self.__block_engine_in.decrypt(header)
if self.__dump_packets:
self._log(DEBUG, util.format_binary(header, 'IN: '));
packet_size = struct.unpack('>I', header[:4])[0]
# leftover contains decrypted bytes from the first block (after the length field)
leftover = header[4:]
if (packet_size - len(leftover)) % self.__block_size_in != 0:
raise SSHException('Invalid packet blocking')
buf = self.read_all(packet_size + self.__mac_size_in - len(leftover))
packet = buf[:packet_size - len(leftover)]
post_packet = buf[packet_size - len(leftover):]
if self.__block_engine_in != None:
packet = self.__block_engine_in.decrypt(packet)
if self.__dump_packets:
self._log(DEBUG, util.format_binary(packet, 'IN: '));
packet = leftover + packet
if self.__mac_size_in > 0:
mac = post_packet[:self.__mac_size_in]
mac_payload = struct.pack('>II', self.__sequence_number_in, packet_size) + packet
my_mac = compute_hmac(self.__mac_key_in, mac_payload, self.__mac_engine_in)[:self.__mac_size_in]
if my_mac != mac:
raise SSHException('Mismatched MAC')
padding = ord(packet[0])
payload = packet[1:packet_size - padding]
randpool.add_event()
if self.__dump_packets:
self._log(DEBUG, 'Got payload (%d bytes, %d padding)' % (packet_size, padding))
if self.__compress_engine_in is not None:
payload = self.__compress_engine_in(payload)
msg = Message(payload[1:])
msg.seqno = self.__sequence_number_in
self.__sequence_number_in = (self.__sequence_number_in + 1) & 0xffffffffL
# check for rekey
self.__received_bytes += packet_size + self.__mac_size_in + 4
self.__received_packets += 1
if self.__need_rekey:
# we've asked to rekey -- give them 20 packets to comply before
# dropping the connection
self.__received_packets_overflow += 1
if self.__received_packets_overflow >= 20:
raise SSHException('Remote transport is ignoring rekey requests')
elif (self.__received_packets >= self.REKEY_PACKETS) or \
(self.__received_bytes >= self.REKEY_BYTES):
# only ask once for rekeying
self._log(DEBUG, 'Rekeying (hit %d packets, %d bytes received)' %
(self.__received_packets, self.__received_bytes))
self.__received_packets_overflow = 0
self._trigger_rekey()
cmd = ord(payload[0])
if cmd in MSG_NAMES:
cmd_name = MSG_NAMES[cmd]
else:
cmd_name = '$%x' % cmd
if self.__dump_packets:
self._log(DEBUG, 'Read packet <%s>, length %d' % (cmd_name, len(payload)))
return cmd, msg
########## protected
def _log(self, level, msg):
if self.__logger is None:
return
if issubclass(type(msg), list):
for m in msg:
self.__logger.log(level, m)
else:
self.__logger.log(level, msg)
def _check_keepalive(self):
if (not self.__keepalive_interval) or (not self.__block_engine_out) or \
self.__need_rekey:
# wait till we're encrypting, and not in the middle of rekeying
return
now = time.time()
if now > self.__keepalive_last + self.__keepalive_interval:
self.__keepalive_callback()
self.__keepalive_last = now
def _py22_read_all(self, n, out):
while n > 0:
r, w, e = select.select([self.__socket], [], [], 0.1)
if self.__socket not in r:
if self.__closed:
raise EOFError()
self._check_keepalive()
else:
x = self.__socket.recv(n)
if len(x) == 0:
raise EOFError()
out += x
n -= len(x)
return out
def _py22_read_timeout(self, timeout):
start = time.time()
while True:
r, w, e = select.select([self.__socket], [], [], 0.1)
if self.__socket in r:
x = self.__socket.recv(1)
if len(x) == 0:
raise EOFError()
break
if self.__closed:
raise EOFError()
now = time.time()
if now - start >= timeout:
raise socket.timeout()
return x
def _read_timeout(self, timeout):
if PY22:
return self._py22_read_timeout(timeout)
start = time.time()
while True:
try:
x = self.__socket.recv(128)
if len(x) == 0:
raise EOFError()
break
except socket.timeout:
pass
if self.__closed:
raise EOFError()
now = time.time()
if now - start >= timeout:
raise socket.timeout()
return x
def _build_packet(self, payload):
# pad up at least 4 bytes, to nearest block-size (usually 8)
bsize = self.__block_size_out
padding = 3 + bsize - ((len(payload) + 8) % bsize)
packet = struct.pack('>IB', len(payload) + padding + 1, padding)
packet += payload
if self.__block_engine_out is not None:
packet += randpool.get_bytes(padding)
else:
# cute trick i caught openssh doing: if we're not encrypting,
# don't waste random bytes for the padding
packet += (chr(0) * padding)
return packet
def _trigger_rekey(self):
# outside code should check for this flag
self.__need_rekey = True

View File

@ -1,144 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
Abstraction of a one-way pipe where the read end can be used in select().
Normally this is trivial, but Windows makes it nearly impossible.
The pipe acts like an Event, which can be set or cleared. When set, the pipe
will trigger as readable in select().
"""
import sys
import os
import socket
def make_pipe ():
if sys.platform[:3] != 'win':
p = PosixPipe()
else:
p = WindowsPipe()
return p
class PosixPipe (object):
def __init__ (self):
self._rfd, self._wfd = os.pipe()
self._set = False
self._forever = False
self._closed = False
def close (self):
os.close(self._rfd)
os.close(self._wfd)
# used for unit tests:
self._closed = True
def fileno (self):
return self._rfd
def clear (self):
if not self._set or self._forever:
return
os.read(self._rfd, 1)
self._set = False
def set (self):
if self._set or self._closed:
return
self._set = True
os.write(self._wfd, '*')
def set_forever (self):
self._forever = True
self.set()
class WindowsPipe (object):
"""
On Windows, only an OS-level "WinSock" may be used in select(), but reads
and writes must be to the actual socket object.
"""
def __init__ (self):
serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serv.bind(('127.0.0.1', 0))
serv.listen(1)
# need to save sockets in _rsock/_wsock so they don't get closed
self._rsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._rsock.connect(('127.0.0.1', serv.getsockname()[1]))
self._wsock, addr = serv.accept()
serv.close()
self._set = False
self._forever = False
self._closed = False
def close (self):
self._rsock.close()
self._wsock.close()
# used for unit tests:
self._closed = True
def fileno (self):
return self._rsock.fileno()
def clear (self):
if not self._set or self._forever:
return
self._rsock.recv(1)
self._set = False
def set (self):
if self._set or self._closed:
return
self._set = True
self._wsock.send('*')
def set_forever (self):
self._forever = True
self.set()
class OrPipe (object):
def __init__(self, pipe):
self._set = False
self._partner = None
self._pipe = pipe
def set(self):
self._set = True
if not self._partner._set:
self._pipe.set()
def clear(self):
self._set = False
if not self._partner._set:
self._pipe.clear()
def make_or_pipe(pipe):
"""
wraps a pipe into two pipe-like objects which are "or"d together to
affect the real pipe. if either returned pipe is set, the wrapped pipe
is set. when both are cleared, the wrapped pipe is cleared.
"""
p1 = OrPipe(pipe)
p2 = OrPipe(pipe)
p1._partner = p2
p2._partner = p1
return p1, p2

View File

@ -1,377 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
Common API for all public keys.
"""
import base64
from binascii import hexlify, unhexlify
import os
from Crypto.Hash import MD5
from Crypto.Cipher import DES3
from paramiko.common import *
from paramiko import util
from paramiko.message import Message
from paramiko.ssh_exception import SSHException, PasswordRequiredException
class PKey (object):
"""
Base class for public keys.
"""
# known encryption types for private key files:
_CIPHER_TABLE = {
'DES-EDE3-CBC': { 'cipher': DES3, 'keysize': 24, 'blocksize': 8, 'mode': DES3.MODE_CBC }
}
def __init__(self, msg=None, data=None):
"""
Create a new instance of this public key type. If C{msg} is given,
the key's public part(s) will be filled in from the message. If
C{data} is given, the key's public part(s) will be filled in from
the string.
@param msg: an optional SSH L{Message} containing a public key of this
type.
@type msg: L{Message}
@param data: an optional string containing a public key of this type
@type data: str
@raise SSHException: if a key cannot be created from the C{data} or
C{msg} given, or no key was passed in.
"""
pass
def __str__(self):
"""
Return a string of an SSH L{Message} made up of the public part(s) of
this key. This string is suitable for passing to L{__init__} to
re-create the key object later.
@return: string representation of an SSH key message.
@rtype: str
"""
return ''
def __cmp__(self, other):
"""
Compare this key to another. Returns 0 if this key is equivalent to
the given key, or non-0 if they are different. Only the public parts
of the key are compared, so a public key will compare equal to its
corresponding private key.
@param other: key to compare to.
@type other: L{PKey}
@return: 0 if the two keys are equivalent, non-0 otherwise.
@rtype: int
"""
hs = hash(self)
ho = hash(other)
if hs != ho:
return cmp(hs, ho)
return cmp(str(self), str(other))
def get_name(self):
"""
Return the name of this private key implementation.
@return: name of this private key type, in SSH terminology (for
example, C{"ssh-rsa"}).
@rtype: str
"""
return ''
def get_bits(self):
"""
Return the number of significant bits in this key. This is useful
for judging the relative security of a key.
@return: bits in the key.
@rtype: int
"""
return 0
def can_sign(self):
"""
Return C{True} if this key has the private part necessary for signing
data.
@return: C{True} if this is a private key.
@rtype: bool
"""
return False
def get_fingerprint(self):
"""
Return an MD5 fingerprint of the public part of this key. Nothing
secret is revealed.
@return: a 16-byte string (binary) of the MD5 fingerprint, in SSH
format.
@rtype: str
"""
return MD5.new(str(self)).digest()
def get_base64(self):
"""
Return a base64 string containing the public part of this key. Nothing
secret is revealed. This format is compatible with that used to store
public key files or recognized host keys.
@return: a base64 string containing the public part of the key.
@rtype: str
"""
return base64.encodestring(str(self)).replace('\n', '')
def sign_ssh_data(self, randpool, data):
"""
Sign a blob of data with this private key, and return a L{Message}
representing an SSH signature message.
@param randpool: a secure random number generator.
@type randpool: L{Crypto.Util.randpool.RandomPool}
@param data: the data to sign.
@type data: str
@return: an SSH signature message.
@rtype: L{Message}
"""
return ''
def verify_ssh_sig(self, data, msg):
"""
Given a blob of data, and an SSH message representing a signature of
that data, verify that it was signed with this key.
@param data: the data that was signed.
@type data: str
@param msg: an SSH signature message
@type msg: L{Message}
@return: C{True} if the signature verifies correctly; C{False}
otherwise.
@rtype: boolean
"""
return False
def from_private_key_file(cls, filename, password=None):
"""
Create a key object by reading a private key file. If the private
key is encrypted and C{password} is not C{None}, the given password
will be used to decrypt the key (otherwise L{PasswordRequiredException}
is thrown). Through the magic of python, this factory method will
exist in all subclasses of PKey (such as L{RSAKey} or L{DSSKey}), but
is useless on the abstract PKey class.
@param filename: name of the file to read
@type filename: str
@param password: an optional password to use to decrypt the key file,
if it's encrypted
@type password: str
@return: a new key object based on the given private key
@rtype: L{PKey}
@raise IOError: if there was an error reading the file
@raise PasswordRequiredException: if the private key file is
encrypted, and C{password} is C{None}
@raise SSHException: if the key file is invalid
"""
key = cls(filename=filename, password=password)
return key
from_private_key_file = classmethod(from_private_key_file)
def from_private_key(cls, file_obj, password=None):
"""
Create a key object by reading a private key from a file (or file-like)
object. If the private key is encrypted and C{password} is not C{None},
the given password will be used to decrypt the key (otherwise
L{PasswordRequiredException} is thrown).
@param file_obj: the file to read from
@type file_obj: file
@param password: an optional password to use to decrypt the key, if it's
encrypted
@type password: str
@return: a new key object based on the given private key
@rtype: L{PKey}
@raise IOError: if there was an error reading the key
@raise PasswordRequiredException: if the private key file is encrypted,
and C{password} is C{None}
@raise SSHException: if the key file is invalid
"""
key = cls(file_obj=file_obj, password=password)
return key
from_private_key = classmethod(from_private_key)
def write_private_key_file(self, filename, password=None):
"""
Write private key contents into a file. If the password is not
C{None}, the key is encrypted before writing.
@param filename: name of the file to write
@type filename: str
@param password: an optional password to use to encrypt the key file
@type password: str
@raise IOError: if there was an error writing the file
@raise SSHException: if the key is invalid
"""
raise Exception('Not implemented in PKey')
def write_private_key(self, file_obj, password=None):
"""
Write private key contents into a file (or file-like) object. If the
password is not C{None}, the key is encrypted before writing.
@param file_obj: the file object to write into
@type file_obj: file
@param password: an optional password to use to encrypt the key
@type password: str
@raise IOError: if there was an error writing to the file
@raise SSHException: if the key is invalid
"""
raise Exception('Not implemented in PKey')
def _read_private_key_file(self, tag, filename, password=None):
"""
Read an SSH2-format private key file, looking for a string of the type
C{"BEGIN xxx PRIVATE KEY"} for some C{xxx}, base64-decode the text we
find, and return it as a string. If the private key is encrypted and
C{password} is not C{None}, the given password will be used to decrypt
the key (otherwise L{PasswordRequiredException} is thrown).
@param tag: C{"RSA"} or C{"DSA"}, the tag used to mark the data block.
@type tag: str
@param filename: name of the file to read.
@type filename: str
@param password: an optional password to use to decrypt the key file,
if it's encrypted.
@type password: str
@return: data blob that makes up the private key.
@rtype: str
@raise IOError: if there was an error reading the file.
@raise PasswordRequiredException: if the private key file is
encrypted, and C{password} is C{None}.
@raise SSHException: if the key file is invalid.
"""
f = open(filename, 'r')
data = self._read_private_key(tag, f, password)
f.close()
return data
def _read_private_key(self, tag, f, password=None):
lines = f.readlines()
start = 0
while (start < len(lines)) and (lines[start].strip() != '-----BEGIN ' + tag + ' PRIVATE KEY-----'):
start += 1
if start >= len(lines):
raise SSHException('not a valid ' + tag + ' private key file')
# parse any headers first
headers = {}
start += 1
while start < len(lines):
l = lines[start].split(': ')
if len(l) == 1:
break
headers[l[0].lower()] = l[1].strip()
start += 1
# find end
end = start
while (lines[end].strip() != '-----END ' + tag + ' PRIVATE KEY-----') and (end < len(lines)):
end += 1
# if we trudged to the end of the file, just try to cope.
try:
data = base64.decodestring(''.join(lines[start:end]))
except base64.binascii.Error, e:
raise SSHException('base64 decoding error: ' + str(e))
if 'proc-type' not in headers:
# unencryped: done
return data
# encrypted keyfile: will need a password
if headers['proc-type'] != '4,ENCRYPTED':
raise SSHException('Unknown private key structure "%s"' % headers['proc-type'])
try:
encryption_type, saltstr = headers['dek-info'].split(',')
except:
raise SSHException('Can\'t parse DEK-info in private key file')
if encryption_type not in self._CIPHER_TABLE:
raise SSHException('Unknown private key cipher "%s"' % encryption_type)
# if no password was passed in, raise an exception pointing out that we need one
if password is None:
raise PasswordRequiredException('Private key file is encrypted')
cipher = self._CIPHER_TABLE[encryption_type]['cipher']
keysize = self._CIPHER_TABLE[encryption_type]['keysize']
mode = self._CIPHER_TABLE[encryption_type]['mode']
salt = unhexlify(saltstr)
key = util.generate_key_bytes(MD5, salt, password, keysize)
return cipher.new(key, mode, salt).decrypt(data)
def _write_private_key_file(self, tag, filename, data, password=None):
"""
Write an SSH2-format private key file in a form that can be read by
paramiko or openssh. If no password is given, the key is written in
a trivially-encoded format (base64) which is completely insecure. If
a password is given, DES-EDE3-CBC is used.
@param tag: C{"RSA"} or C{"DSA"}, the tag used to mark the data block.
@type tag: str
@param filename: name of the file to write.
@type filename: str
@param data: data blob that makes up the private key.
@type data: str
@param password: an optional password to use to encrypt the file.
@type password: str
@raise IOError: if there was an error writing the file.
"""
f = open(filename, 'w', 0600)
# grrr... the mode doesn't always take hold
os.chmod(filename, 0600)
self._write_private_key(tag, f, data, password)
f.close()
def _write_private_key(self, tag, f, data, password=None):
f.write('-----BEGIN %s PRIVATE KEY-----\n' % tag)
if password is not None:
# since we only support one cipher here, use it
cipher_name = self._CIPHER_TABLE.keys()[0]
cipher = self._CIPHER_TABLE[cipher_name]['cipher']
keysize = self._CIPHER_TABLE[cipher_name]['keysize']
blocksize = self._CIPHER_TABLE[cipher_name]['blocksize']
mode = self._CIPHER_TABLE[cipher_name]['mode']
salt = randpool.get_bytes(8)
key = util.generate_key_bytes(MD5, salt, password, keysize)
if len(data) % blocksize != 0:
n = blocksize - len(data) % blocksize
#data += randpool.get_bytes(n)
# that would make more sense ^, but it confuses openssh.
data += '\0' * n
data = cipher.new(key, mode, salt).encrypt(data)
f.write('Proc-Type: 4,ENCRYPTED\n')
f.write('DEK-Info: %s,%s\n' % (cipher_name, hexlify(salt).upper()))
f.write('\n')
s = base64.encodestring(data)
# re-wrap to 64-char lines
s = ''.join(s.split('\n'))
s = '\n'.join([s[i : i+64] for i in range(0, len(s), 64)])
f.write(s)
f.write('\n')
f.write('-----END %s PRIVATE KEY-----\n' % tag)

View File

@ -1,148 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
Utility functions for dealing with primes.
"""
from Crypto.Util import number
from paramiko import util
from paramiko.ssh_exception import SSHException
def _generate_prime(bits, randpool):
"primtive attempt at prime generation"
hbyte_mask = pow(2, bits % 8) - 1
while True:
# loop catches the case where we increment n into a higher bit-range
x = randpool.get_bytes((bits+7) // 8)
if hbyte_mask > 0:
x = chr(ord(x[0]) & hbyte_mask) + x[1:]
n = util.inflate_long(x, 1)
n |= 1
n |= (1 << (bits - 1))
while not number.isPrime(n):
n += 2
if util.bit_length(n) == bits:
break
return n
def _roll_random(rpool, n):
"returns a random # from 0 to N-1"
bits = util.bit_length(n-1)
bytes = (bits + 7) // 8
hbyte_mask = pow(2, bits % 8) - 1
# so here's the plan:
# we fetch as many random bits as we'd need to fit N-1, and if the
# generated number is >= N, we try again. in the worst case (N-1 is a
# power of 2), we have slightly better than 50% odds of getting one that
# fits, so i can't guarantee that this loop will ever finish, but the odds
# of it looping forever should be infinitesimal.
while True:
x = rpool.get_bytes(bytes)
if hbyte_mask > 0:
x = chr(ord(x[0]) & hbyte_mask) + x[1:]
num = util.inflate_long(x, 1)
if num < n:
break
return num
class ModulusPack (object):
"""
convenience object for holding the contents of the /etc/ssh/moduli file,
on systems that have such a file.
"""
def __init__(self, rpool):
# pack is a hash of: bits -> [ (generator, modulus) ... ]
self.pack = {}
self.discarded = []
self.randpool = rpool
def _parse_modulus(self, line):
timestamp, mod_type, tests, tries, size, generator, modulus = line.split()
mod_type = int(mod_type)
tests = int(tests)
tries = int(tries)
size = int(size)
generator = int(generator)
modulus = long(modulus, 16)
# weed out primes that aren't at least:
# type 2 (meets basic structural requirements)
# test 4 (more than just a small-prime sieve)
# tries < 100 if test & 4 (at least 100 tries of miller-rabin)
if (mod_type < 2) or (tests < 4) or ((tests & 4) and (tests < 8) and (tries < 100)):
self.discarded.append((modulus, 'does not meet basic requirements'))
return
if generator == 0:
generator = 2
# there's a bug in the ssh "moduli" file (yeah, i know: shock! dismay!
# call cnn!) where it understates the bit lengths of these primes by 1.
# this is okay.
bl = util.bit_length(modulus)
if (bl != size) and (bl != size + 1):
self.discarded.append((modulus, 'incorrectly reported bit length %d' % size))
return
if bl not in self.pack:
self.pack[bl] = []
self.pack[bl].append((generator, modulus))
def read_file(self, filename):
"""
@raise IOError: passed from any file operations that fail.
"""
self.pack = {}
f = open(filename, 'r')
for line in f:
line = line.strip()
if (len(line) == 0) or (line[0] == '#'):
continue
try:
self._parse_modulus(line)
except:
continue
f.close()
def get_modulus(self, min, prefer, max):
bitsizes = self.pack.keys()
bitsizes.sort()
if len(bitsizes) == 0:
raise SSHException('no moduli available')
good = -1
# find nearest bitsize >= preferred
for b in bitsizes:
if (b >= prefer) and (b < max) and ((b < good) or (good == -1)):
good = b
# if that failed, find greatest bitsize >= min
if good == -1:
for b in bitsizes:
if (b >= min) and (b < max) and (b > good):
good = b
if good == -1:
# their entire (min, max) range has no intersection with our range.
# if their range is below ours, pick the smallest. otherwise pick
# the largest. it'll be out of their range requirement either way,
# but we'll be sending them the closest one we have.
good = bitsizes[0]
if min > good:
good = bitsizes[-1]
# now pick a random modulus of this bitsize
n = _roll_random(self.randpool, len(self.pack[good]))
return self.pack[good][n]

View File

@ -1,69 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
Resource manager.
"""
import weakref
class ResourceManager (object):
"""
A registry of objects and resources that should be closed when those
objects are deleted.
This is meant to be a safer alternative to python's C{__del__} method,
which can cause reference cycles to never be collected. Objects registered
with the ResourceManager can be collected but still free resources when
they die.
Resources are registered using L{register}, and when an object is garbage
collected, each registered resource is closed by having its C{close()}
method called. Multiple resources may be registered per object, but a
resource will only be closed once, even if multiple objects register it.
(The last object to register it wins.)
"""
def __init__(self):
self._table = {}
def register(self, obj, resource):
"""
Register a resource to be closed with an object is collected.
When the given C{obj} is garbage-collected by the python interpreter,
the C{resource} will be closed by having its C{close()} method called.
Any exceptions are ignored.
@param obj: the object to track
@type obj: object
@param resource: the resource to close when the object is collected
@type resource: object
"""
def callback(ref):
try:
resource.close()
except:
pass
del self._table[id(resource)]
# keep the weakref in a table so it sticks around long enough to get
# its callback called. :)
self._table[id(resource)] = weakref.ref(obj, callback)
# singleton
ResourceManager = ResourceManager()

View File

@ -1,105 +0,0 @@
#!/usr/bin/python
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# -*- coding: ascii -*-
# Copyright (C) 2008 Dwayne C. Litzenberger <dlitz@dlitz.net>
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import sys
import threading
from Crypto.Util.randpool import RandomPool as _RandomPool
try:
import platform
except ImportError:
platform = None # Not available using Python 2.2
def _strxor(a, b):
assert len(a) == len(b)
return "".join(map(lambda x, y: chr(ord(x) ^ ord(y)), a, b))
## Find a strong random entropy source, depending on the detected platform.
## WARNING TO DEVELOPERS: This will fail on some systems, but do NOT use
## Crypto.Util.randpool.RandomPool as a fall-back. RandomPool will happily run
## with very little entropy, thus _silently_ defeating any security that
## Paramiko attempts to provide. (This is current as of PyCrypto 2.0.1).
if ((platform is not None and platform.system().lower() == 'windows') or
sys.platform == 'win32'):
# MS Windows
from paramiko import rng_win32
rng_device = rng_win32.open_rng_device()
else:
# Assume POSIX (any system where /dev/urandom exists)
from paramiko import rng_posix
rng_device = rng_posix.open_rng_device()
class StrongLockingRandomPool(object):
"""Wrapper around RandomPool guaranteeing strong random numbers.
Crypto.Util.randpool.RandomPool will silently operate even if it is seeded
with little or no entropy, and it provides no prediction resistance if its
state is ever compromised throughout its runtime. It is also not thread-safe.
This wrapper augments RandomPool by XORing its output with random bits from
the operating system, and by controlling access to the underlying
RandomPool using an exclusive lock.
"""
def __init__(self, instance=None):
if instance is None:
instance = _RandomPool()
self.randpool = instance
self.randpool_lock = threading.Lock()
self.entropy = rng_device
# Stir 256 bits of entropy from the RNG device into the RandomPool.
self.randpool.stir(self.entropy.read(32))
self.entropy.randomize()
def stir(self, s=''):
self.randpool_lock.acquire()
try:
self.randpool.stir(s)
finally:
self.randpool_lock.release()
self.entropy.randomize()
def randomize(self, N=0):
self.randpool_lock.acquire()
try:
self.randpool.randomize(N)
finally:
self.randpool_lock.release()
self.entropy.randomize()
def add_event(self, s=''):
self.randpool_lock.acquire()
try:
self.randpool.add_event(s)
finally:
self.randpool_lock.release()
def get_bytes(self, N):
self.randpool_lock.acquire()
try:
randpool_data = self.randpool.get_bytes(N)
finally:
self.randpool_lock.release()
entropy_data = self.entropy.read(N)
result = _strxor(randpool_data, entropy_data)
assert len(randpool_data) == N and len(entropy_data) == N and len(result) == N
return result
# vim:set ts=4 sw=4 sts=4 expandtab:

View File

@ -1,93 +0,0 @@
#!/usr/bin/python
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# -*- coding: ascii -*-
# Copyright (C) 2008 Dwayne C. Litzenberger <dlitz@dlitz.net>
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import os
import stat
class error(Exception):
pass
class _RNG(object):
def __init__(self, file):
self.file = file
def read(self, bytes):
return self.file.read(bytes)
def close(self):
return self.file.close()
def randomize(self):
return
def open_rng_device(device_path=None):
"""Open /dev/urandom and perform some sanity checks."""
f = None
g = None
if device_path is None:
device_path = "/dev/urandom"
try:
# Try to open /dev/urandom now so that paramiko will be able to access
# it even if os.chroot() is invoked later.
try:
f = open(device_path, "rb", 0)
except EnvironmentError:
raise error("Unable to open /dev/urandom")
# Open a second file descriptor for sanity checking later.
try:
g = open(device_path, "rb", 0)
except EnvironmentError:
raise error("Unable to open /dev/urandom")
# Check that /dev/urandom is a character special device, not a regular file.
st = os.fstat(f.fileno()) # f
if stat.S_ISREG(st.st_mode) or not stat.S_ISCHR(st.st_mode):
raise error("/dev/urandom is not a character special device")
st = os.fstat(g.fileno()) # g
if stat.S_ISREG(st.st_mode) or not stat.S_ISCHR(st.st_mode):
raise error("/dev/urandom is not a character special device")
# Check that /dev/urandom always returns the number of bytes requested
x = f.read(20)
y = g.read(20)
if len(x) != 20 or len(y) != 20:
raise error("Error reading from /dev/urandom: input truncated")
# Check that different reads return different data
if x == y:
raise error("/dev/urandom is broken; returning identical data: %r == %r" % (x, y))
# Close the duplicate file object
g.close()
# Return the first file object
return _RNG(f)
except error:
if f is not None:
f.close()
if g is not None:
g.close()
raise
# vim:set ts=4 sw=4 sts=4 expandtab:

View File

@ -1,117 +0,0 @@
#!/usr/bin/python
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# -*- coding: ascii -*-
# Copyright (C) 2008 Dwayne C. Litzenberger <dlitz@dlitz.net>
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
class error(Exception):
pass
# Try to import the "winrandom" module
try:
from Crypto.Util import winrandom as _winrandom
except ImportError:
_winrandom = None
# Try to import the "urandom" module
try:
from os import urandom as _urandom
except ImportError:
_urandom = None
class _RNG(object):
def __init__(self, readfunc):
self.read = readfunc
def randomize(self):
# According to "Cryptanalysis of the Random Number Generator of the
# Windows Operating System", by Leo Dorrendorf and Zvi Gutterman
# and Benny Pinkas <http://eprint.iacr.org/2007/419>,
# CryptGenRandom only updates its internal state using kernel-provided
# random data every 128KiB of output.
self.read(128*1024) # discard 128 KiB of output
def _open_winrandom():
if _winrandom is None:
raise error("Crypto.Util.winrandom module not found")
# Check that we can open the winrandom module
try:
r0 = _winrandom.new()
r1 = _winrandom.new()
except Exception, exc:
raise error("winrandom.new() failed: %s" % str(exc), exc)
# Check that we can read from the winrandom module
try:
x = r0.get_bytes(20)
y = r1.get_bytes(20)
except Exception, exc:
raise error("winrandom get_bytes failed: %s" % str(exc), exc)
# Check that the requested number of bytes are returned
if len(x) != 20 or len(y) != 20:
raise error("Error reading from winrandom: input truncated")
# Check that different reads return different data
if x == y:
raise error("winrandom broken: returning identical data")
return _RNG(r0.get_bytes)
def _open_urandom():
if _urandom is None:
raise error("os.urandom function not found")
# Check that we can read from os.urandom()
try:
x = _urandom(20)
y = _urandom(20)
except Exception, exc:
raise error("os.urandom failed: %s" % str(exc), exc)
# Check that the requested number of bytes are returned
if len(x) != 20 or len(y) != 20:
raise error("os.urandom failed: input truncated")
# Check that different reads return different data
if x == y:
raise error("os.urandom failed: returning identical data")
return _RNG(_urandom)
def open_rng_device():
# Try using the Crypto.Util.winrandom module
try:
return _open_winrandom()
except error:
pass
# Several versions of PyCrypto do not contain the winrandom module, but
# Python >= 2.4 has os.urandom, so try to use that.
try:
return _open_urandom()
except error:
pass
# SECURITY NOTE: DO NOT USE Crypto.Util.randpool.RandomPool HERE!
# If we got to this point, RandomPool will silently run with very little
# entropy. (This is current as of PyCrypto 2.0.1).
# See http://www.lag.net/pipermail/paramiko/2008-January/000599.html
# and http://www.lag.net/pipermail/paramiko/2008-April/000678.html
raise error("Unable to find a strong random entropy source. You cannot run this software securely under the current configuration.")
# vim:set ts=4 sw=4 sts=4 expandtab:

View File

@ -1,183 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
L{RSAKey}
"""
from Crypto.PublicKey import RSA
from Crypto.Hash import SHA, MD5
from Crypto.Cipher import DES3
from paramiko.common import *
from paramiko import util
from paramiko.message import Message
from paramiko.ber import BER, BERException
from paramiko.pkey import PKey
from paramiko.ssh_exception import SSHException
class RSAKey (PKey):
"""
Representation of an RSA key which can be used to sign and verify SSH2
data.
"""
def __init__(self, msg=None, data=None, filename=None, password=None, vals=None, file_obj=None):
self.n = None
self.e = None
self.d = None
self.p = None
self.q = None
if file_obj is not None:
self._from_private_key(file_obj, password)
return
if filename is not None:
self._from_private_key_file(filename, password)
return
if (msg is None) and (data is not None):
msg = Message(data)
if vals is not None:
self.e, self.n = vals
else:
if msg is None:
raise SSHException('Key object may not be empty')
if msg.get_string() != 'ssh-rsa':
raise SSHException('Invalid key')
self.e = msg.get_mpint()
self.n = msg.get_mpint()
self.size = util.bit_length(self.n)
def __str__(self):
m = Message()
m.add_string('ssh-rsa')
m.add_mpint(self.e)
m.add_mpint(self.n)
return str(m)
def __hash__(self):
h = hash(self.get_name())
h = h * 37 + hash(self.e)
h = h * 37 + hash(self.n)
return hash(h)
def get_name(self):
return 'ssh-rsa'
def get_bits(self):
return self.size
def can_sign(self):
return self.d is not None
def sign_ssh_data(self, rpool, data):
digest = SHA.new(data).digest()
rsa = RSA.construct((long(self.n), long(self.e), long(self.d)))
sig = util.deflate_long(rsa.sign(self._pkcs1imify(digest), '')[0], 0)
m = Message()
m.add_string('ssh-rsa')
m.add_string(sig)
return m
def verify_ssh_sig(self, data, msg):
if msg.get_string() != 'ssh-rsa':
return False
sig = util.inflate_long(msg.get_string(), True)
# verify the signature by SHA'ing the data and encrypting it using the
# public key. some wackiness ensues where we "pkcs1imify" the 20-byte
# hash into a string as long as the RSA key.
hash_obj = util.inflate_long(self._pkcs1imify(SHA.new(data).digest()), True)
rsa = RSA.construct((long(self.n), long(self.e)))
return rsa.verify(hash_obj, (sig,))
def _encode_key(self):
if (self.p is None) or (self.q is None):
raise SSHException('Not enough key info to write private key file')
keylist = [ 0, self.n, self.e, self.d, self.p, self.q,
self.d % (self.p - 1), self.d % (self.q - 1),
util.mod_inverse(self.q, self.p) ]
try:
b = BER()
b.encode(keylist)
except BERException:
raise SSHException('Unable to create ber encoding of key')
return str(b)
def write_private_key_file(self, filename, password=None):
self._write_private_key_file('RSA', filename, self._encode_key(), password)
def write_private_key(self, file_obj, password=None):
self._write_private_key('RSA', file_obj, self._encode_key(), password)
def generate(bits, progress_func=None):
"""
Generate a new private RSA key. This factory function can be used to
generate a new host key or authentication key.
@param bits: number of bits the generated key should be.
@type bits: int
@param progress_func: an optional function to call at key points in
key generation (used by C{pyCrypto.PublicKey}).
@type progress_func: function
@return: new private key
@rtype: L{RSAKey}
"""
randpool.stir()
rsa = RSA.generate(bits, randpool.get_bytes, progress_func)
key = RSAKey(vals=(rsa.e, rsa.n))
key.d = rsa.d
key.p = rsa.p
key.q = rsa.q
return key
generate = staticmethod(generate)
### internals...
def _pkcs1imify(self, data):
"""
turn a 20-byte SHA1 hash into a blob of data as large as the key's N,
using PKCS1's \"emsa-pkcs1-v1_5\" encoding. totally bizarre.
"""
SHA1_DIGESTINFO = '\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'
size = len(util.deflate_long(self.n, 0))
filler = '\xff' * (size - len(SHA1_DIGESTINFO) - len(data) - 3)
return '\x00\x01' + filler + '\x00' + SHA1_DIGESTINFO + data
def _from_private_key_file(self, filename, password):
data = self._read_private_key_file('RSA', filename, password)
self._decode_key(data)
def _from_private_key(self, file_obj, password):
data = self._read_private_key('RSA', file_obj, password)
self._decode_key(data)
def _decode_key(self, data):
# private key file contains:
# RSAPrivateKey = { version = 0, n, e, d, p, q, d mod p-1, d mod q-1, q**-1 mod p }
try:
keylist = BER(data).decode()
except BERException:
raise SSHException('Unable to parse key file')
if (type(keylist) is not list) or (len(keylist) < 4) or (keylist[0] != 0):
raise SSHException('Not a valid RSA private key file (bad ber encoding)')
self.n = keylist[1]
self.e = keylist[2]
self.d = keylist[3]
# not really needed
self.p = keylist[4]
self.q = keylist[5]
self.size = util.bit_length(self.n)

View File

@ -1,629 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
L{ServerInterface} is an interface to override for server support.
"""
import threading
from paramiko.common import *
from paramiko import util
class InteractiveQuery (object):
"""
A query (set of prompts) for a user during interactive authentication.
"""
def __init__(self, name='', instructions='', *prompts):
"""
Create a new interactive query to send to the client. The name and
instructions are optional, but are generally displayed to the end
user. A list of prompts may be included, or they may be added via
the L{add_prompt} method.
@param name: name of this query
@type name: str
@param instructions: user instructions (usually short) about this query
@type instructions: str
@param prompts: one or more authentication prompts
@type prompts: str
"""
self.name = name
self.instructions = instructions
self.prompts = []
for x in prompts:
if (type(x) is str) or (type(x) is unicode):
self.add_prompt(x)
else:
self.add_prompt(x[0], x[1])
def add_prompt(self, prompt, echo=True):
"""
Add a prompt to this query. The prompt should be a (reasonably short)
string. Multiple prompts can be added to the same query.
@param prompt: the user prompt
@type prompt: str
@param echo: C{True} (default) if the user's response should be echoed;
C{False} if not (for a password or similar)
@type echo: bool
"""
self.prompts.append((prompt, echo))
class ServerInterface (object):
"""
This class defines an interface for controlling the behavior of paramiko
in server mode.
Methods on this class are called from paramiko's primary thread, so you
shouldn't do too much work in them. (Certainly nothing that blocks or
sleeps.)
"""
def check_channel_request(self, kind, chanid):
"""
Determine if a channel request of a given type will be granted, and
return C{OPEN_SUCCEEDED} or an error code. This method is
called in server mode when the client requests a channel, after
authentication is complete.
If you allow channel requests (and an ssh server that didn't would be
useless), you should also override some of the channel request methods
below, which are used to determine which services will be allowed on
a given channel:
- L{check_channel_pty_request}
- L{check_channel_shell_request}
- L{check_channel_subsystem_request}
- L{check_channel_window_change_request}
- L{check_channel_x11_request}
The C{chanid} parameter is a small number that uniquely identifies the
channel within a L{Transport}. A L{Channel} object is not created
unless this method returns C{OPEN_SUCCEEDED} -- once a
L{Channel} object is created, you can call L{Channel.get_id} to
retrieve the channel ID.
The return value should either be C{OPEN_SUCCEEDED} (or
C{0}) to allow the channel request, or one of the following error
codes to reject it:
- C{OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED}
- C{OPEN_FAILED_CONNECT_FAILED}
- C{OPEN_FAILED_UNKNOWN_CHANNEL_TYPE}
- C{OPEN_FAILED_RESOURCE_SHORTAGE}
The default implementation always returns
C{OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED}.
@param kind: the kind of channel the client would like to open
(usually C{"session"}).
@type kind: str
@param chanid: ID of the channel
@type chanid: int
@return: a success or failure code (listed above)
@rtype: int
"""
return OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
def get_allowed_auths(self, username):
"""
Return a list of authentication methods supported by the server.
This list is sent to clients attempting to authenticate, to inform them
of authentication methods that might be successful.
The "list" is actually a string of comma-separated names of types of
authentication. Possible values are C{"password"}, C{"publickey"},
and C{"none"}.
The default implementation always returns C{"password"}.
@param username: the username requesting authentication.
@type username: str
@return: a comma-separated list of authentication types
@rtype: str
"""
return 'password'
def check_auth_none(self, username):
"""
Determine if a client may open channels with no (further)
authentication.
Return L{AUTH_FAILED} if the client must authenticate, or
L{AUTH_SUCCESSFUL} if it's okay for the client to not
authenticate.
The default implementation always returns L{AUTH_FAILED}.
@param username: the username of the client.
@type username: str
@return: L{AUTH_FAILED} if the authentication fails;
L{AUTH_SUCCESSFUL} if it succeeds.
@rtype: int
"""
return AUTH_FAILED
def check_auth_password(self, username, password):
"""
Determine if a given username and password supplied by the client is
acceptable for use in authentication.
Return L{AUTH_FAILED} if the password is not accepted,
L{AUTH_SUCCESSFUL} if the password is accepted and completes
the authentication, or L{AUTH_PARTIALLY_SUCCESSFUL} if your
authentication is stateful, and this key is accepted for
authentication, but more authentication is required. (In this latter
case, L{get_allowed_auths} will be called to report to the client what
options it has for continuing the authentication.)
The default implementation always returns L{AUTH_FAILED}.
@param username: the username of the authenticating client.
@type username: str
@param password: the password given by the client.
@type password: str
@return: L{AUTH_FAILED} if the authentication fails;
L{AUTH_SUCCESSFUL} if it succeeds;
L{AUTH_PARTIALLY_SUCCESSFUL} if the password auth is
successful, but authentication must continue.
@rtype: int
"""
return AUTH_FAILED
def check_auth_publickey(self, username, key):
"""
Determine if a given key supplied by the client is acceptable for use
in authentication. You should override this method in server mode to
check the username and key and decide if you would accept a signature
made using this key.
Return L{AUTH_FAILED} if the key is not accepted,
L{AUTH_SUCCESSFUL} if the key is accepted and completes the
authentication, or L{AUTH_PARTIALLY_SUCCESSFUL} if your
authentication is stateful, and this password is accepted for
authentication, but more authentication is required. (In this latter
case, L{get_allowed_auths} will be called to report to the client what
options it has for continuing the authentication.)
Note that you don't have to actually verify any key signtature here.
If you're willing to accept the key, paramiko will do the work of
verifying the client's signature.
The default implementation always returns L{AUTH_FAILED}.
@param username: the username of the authenticating client
@type username: str
@param key: the key object provided by the client
@type key: L{PKey <pkey.PKey>}
@return: L{AUTH_FAILED} if the client can't authenticate
with this key; L{AUTH_SUCCESSFUL} if it can;
L{AUTH_PARTIALLY_SUCCESSFUL} if it can authenticate with
this key but must continue with authentication
@rtype: int
"""
return AUTH_FAILED
def check_auth_interactive(self, username, submethods):
"""
Begin an interactive authentication challenge, if supported. You
should override this method in server mode if you want to support the
C{"keyboard-interactive"} auth type, which requires you to send a
series of questions for the client to answer.
Return L{AUTH_FAILED} if this auth method isn't supported. Otherwise,
you should return an L{InteractiveQuery} object containing the prompts
and instructions for the user. The response will be sent via a call
to L{check_auth_interactive_response}.
The default implementation always returns L{AUTH_FAILED}.
@param username: the username of the authenticating client
@type username: str
@param submethods: a comma-separated list of methods preferred by the
client (usually empty)
@type submethods: str
@return: L{AUTH_FAILED} if this auth method isn't supported; otherwise
an object containing queries for the user
@rtype: int or L{InteractiveQuery}
"""
return AUTH_FAILED
def check_auth_interactive_response(self, responses):
"""
Continue or finish an interactive authentication challenge, if
supported. You should override this method in server mode if you want
to support the C{"keyboard-interactive"} auth type.
Return L{AUTH_FAILED} if the responses are not accepted,
L{AUTH_SUCCESSFUL} if the responses are accepted and complete
the authentication, or L{AUTH_PARTIALLY_SUCCESSFUL} if your
authentication is stateful, and this set of responses is accepted for
authentication, but more authentication is required. (In this latter
case, L{get_allowed_auths} will be called to report to the client what
options it has for continuing the authentication.)
If you wish to continue interactive authentication with more questions,
you may return an L{InteractiveQuery} object, which should cause the
client to respond with more answers, calling this method again. This
cycle can continue indefinitely.
The default implementation always returns L{AUTH_FAILED}.
@param responses: list of responses from the client
@type responses: list(str)
@return: L{AUTH_FAILED} if the authentication fails;
L{AUTH_SUCCESSFUL} if it succeeds;
L{AUTH_PARTIALLY_SUCCESSFUL} if the interactive auth is
successful, but authentication must continue; otherwise an object
containing queries for the user
@rtype: int or L{InteractiveQuery}
"""
return AUTH_FAILED
def check_port_forward_request(self, address, port):
"""
Handle a request for port forwarding. The client is asking that
connections to the given address and port be forwarded back across
this ssh connection. An address of C{"0.0.0.0"} indicates a global
address (any address associated with this server) and a port of C{0}
indicates that no specific port is requested (usually the OS will pick
a port).
The default implementation always returns C{False}, rejecting the
port forwarding request. If the request is accepted, you should return
the port opened for listening.
@param address: the requested address
@type address: str
@param port: the requested port
@type port: int
@return: the port number that was opened for listening, or C{False} to
reject
@rtype: int
"""
return False
def cancel_port_forward_request(self, address, port):
"""
The client would like to cancel a previous port-forwarding request.
If the given address and port is being forwarded across this ssh
connection, the port should be closed.
@param address: the forwarded address
@type address: str
@param port: the forwarded port
@type port: int
"""
pass
def check_global_request(self, kind, msg):
"""
Handle a global request of the given C{kind}. This method is called
in server mode and client mode, whenever the remote host makes a global
request. If there are any arguments to the request, they will be in
C{msg}.
There aren't any useful global requests defined, aside from port
forwarding, so usually this type of request is an extension to the
protocol.
If the request was successful and you would like to return contextual
data to the remote host, return a tuple. Items in the tuple will be
sent back with the successful result. (Note that the items in the
tuple can only be strings, ints, longs, or bools.)
The default implementation always returns C{False}, indicating that it
does not support any global requests.
@note: Port forwarding requests are handled separately, in
L{check_port_forward_request}.
@param kind: the kind of global request being made.
@type kind: str
@param msg: any extra arguments to the request.
@type msg: L{Message}
@return: C{True} or a tuple of data if the request was granted;
C{False} otherwise.
@rtype: bool
"""
return False
### Channel requests
def check_channel_pty_request(self, channel, term, width, height, pixelwidth, pixelheight,
modes):
"""
Determine if a pseudo-terminal of the given dimensions (usually
requested for shell access) can be provided on the given channel.
The default implementation always returns C{False}.
@param channel: the L{Channel} the pty request arrived on.
@type channel: L{Channel}
@param term: type of terminal requested (for example, C{"vt100"}).
@type term: str
@param width: width of screen in characters.
@type width: int
@param height: height of screen in characters.
@type height: int
@param pixelwidth: width of screen in pixels, if known (may be C{0} if
unknown).
@type pixelwidth: int
@param pixelheight: height of screen in pixels, if known (may be C{0}
if unknown).
@type pixelheight: int
@return: C{True} if the psuedo-terminal has been allocated; C{False}
otherwise.
@rtype: bool
"""
return False
def check_channel_shell_request(self, channel):
"""
Determine if a shell will be provided to the client on the given
channel. If this method returns C{True}, the channel should be
connected to the stdin/stdout of a shell (or something that acts like
a shell).
The default implementation always returns C{False}.
@param channel: the L{Channel} the request arrived on.
@type channel: L{Channel}
@return: C{True} if this channel is now hooked up to a shell; C{False}
if a shell can't or won't be provided.
@rtype: bool
"""
return False
def check_channel_exec_request(self, channel, command):
"""
Determine if a shell command will be executed for the client. If this
method returns C{True}, the channel should be connected to the stdin,
stdout, and stderr of the shell command.
The default implementation always returns C{False}.
@param channel: the L{Channel} the request arrived on.
@type channel: L{Channel}
@param command: the command to execute.
@type command: str
@return: C{True} if this channel is now hooked up to the stdin,
stdout, and stderr of the executing command; C{False} if the
command will not be executed.
@rtype: bool
@since: 1.1
"""
return False
def check_channel_subsystem_request(self, channel, name):
"""
Determine if a requested subsystem will be provided to the client on
the given channel. If this method returns C{True}, all future I/O
through this channel will be assumed to be connected to the requested
subsystem. An example of a subsystem is C{sftp}.
The default implementation checks for a subsystem handler assigned via
L{Transport.set_subsystem_handler}.
If one has been set, the handler is invoked and this method returns
C{True}. Otherwise it returns C{False}.
@note: Because the default implementation uses the L{Transport} to
identify valid subsystems, you probably won't need to override this
method.
@param channel: the L{Channel} the pty request arrived on.
@type channel: L{Channel}
@param name: name of the requested subsystem.
@type name: str
@return: C{True} if this channel is now hooked up to the requested
subsystem; C{False} if that subsystem can't or won't be provided.
@rtype: bool
"""
handler_class, larg, kwarg = channel.get_transport()._get_subsystem_handler(name)
if handler_class is None:
return False
handler = handler_class(channel, name, self, *larg, **kwarg)
handler.start()
return True
def check_channel_window_change_request(self, channel, width, height, pixelwidth, pixelheight):
"""
Determine if the pseudo-terminal on the given channel can be resized.
This only makes sense if a pty was previously allocated on it.
The default implementation always returns C{False}.
@param channel: the L{Channel} the pty request arrived on.
@type channel: L{Channel}
@param width: width of screen in characters.
@type width: int
@param height: height of screen in characters.
@type height: int
@param pixelwidth: width of screen in pixels, if known (may be C{0} if
unknown).
@type pixelwidth: int
@param pixelheight: height of screen in pixels, if known (may be C{0}
if unknown).
@type pixelheight: int
@return: C{True} if the terminal was resized; C{False} if not.
@rtype: bool
"""
return False
def check_channel_x11_request(self, channel, single_connection, auth_protocol, auth_cookie, screen_number):
"""
Determine if the client will be provided with an X11 session. If this
method returns C{True}, X11 applications should be routed through new
SSH channels, using L{Transport.open_x11_channel}.
The default implementation always returns C{False}.
@param channel: the L{Channel} the X11 request arrived on
@type channel: L{Channel}
@param single_connection: C{True} if only a single X11 channel should
be opened
@type single_connection: bool
@param auth_protocol: the protocol used for X11 authentication
@type auth_protocol: str
@param auth_cookie: the cookie used to authenticate to X11
@type auth_cookie: str
@param screen_number: the number of the X11 screen to connect to
@type screen_number: int
@return: C{True} if the X11 session was opened; C{False} if not
@rtype: bool
"""
return False
def check_channel_direct_tcpip_request(self, chanid, origin, destination):
"""
Determine if a local port forwarding channel will be granted, and
return C{OPEN_SUCCEEDED} or an error code. This method is
called in server mode when the client requests a channel, after
authentication is complete.
The C{chanid} parameter is a small number that uniquely identifies the
channel within a L{Transport}. A L{Channel} object is not created
unless this method returns C{OPEN_SUCCEEDED} -- once a
L{Channel} object is created, you can call L{Channel.get_id} to
retrieve the channel ID.
The origin and destination parameters are (ip_address, port) tuples
that correspond to both ends of the TCP connection in the forwarding
tunnel.
The return value should either be C{OPEN_SUCCEEDED} (or
C{0}) to allow the channel request, or one of the following error
codes to reject it:
- C{OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED}
- C{OPEN_FAILED_CONNECT_FAILED}
- C{OPEN_FAILED_UNKNOWN_CHANNEL_TYPE}
- C{OPEN_FAILED_RESOURCE_SHORTAGE}
The default implementation always returns
C{OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED}.
@param chanid: ID of the channel
@type chanid: int
@param origin: 2-tuple containing the IP address and port of the
originator (client side)
@type origin: tuple
@param destination: 2-tuple containing the IP address and port of the
destination (server side)
@type destination: tuple
@return: a success or failure code (listed above)
@rtype: int
"""
return OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
class SubsystemHandler (threading.Thread):
"""
Handler for a subsytem in server mode. If you create a subclass of this
class and pass it to
L{Transport.set_subsystem_handler},
an object of this
class will be created for each request for this subsystem. Each new object
will be executed within its own new thread by calling L{start_subsystem}.
When that method completes, the channel is closed.
For example, if you made a subclass C{MP3Handler} and registered it as the
handler for subsystem C{"mp3"}, then whenever a client has successfully
authenticated and requests subsytem C{"mp3"}, an object of class
C{MP3Handler} will be created, and L{start_subsystem} will be called on
it from a new thread.
"""
def __init__(self, channel, name, server):
"""
Create a new handler for a channel. This is used by L{ServerInterface}
to start up a new handler when a channel requests this subsystem. You
don't need to override this method, but if you do, be sure to pass the
C{channel} and C{name} parameters through to the original C{__init__}
method here.
@param channel: the channel associated with this subsystem request.
@type channel: L{Channel}
@param name: name of the requested subsystem.
@type name: str
@param server: the server object for the session that started this
subsystem
@type server: L{ServerInterface}
"""
threading.Thread.__init__(self, target=self._run)
self.__channel = channel
self.__transport = channel.get_transport()
self.__name = name
self.__server = server
def get_server(self):
"""
Return the L{ServerInterface} object associated with this channel and
subsystem.
@rtype: L{ServerInterface}
"""
return self.__server
def _run(self):
try:
self.__transport._log(DEBUG, 'Starting handler for subsystem %s' % self.__name)
self.start_subsystem(self.__name, self.__transport, self.__channel)
except Exception, e:
self.__transport._log(ERROR, 'Exception in subsystem handler for "%s": %s' %
(self.__name, str(e)))
self.__transport._log(ERROR, util.tb_strings())
try:
self.finish_subsystem()
except:
pass
def start_subsystem(self, name, transport, channel):
"""
Process an ssh subsystem in server mode. This method is called on a
new object (and in a new thread) for each subsystem request. It is
assumed that all subsystem logic will take place here, and when the
subsystem is finished, this method will return. After this method
returns, the channel is closed.
The combination of C{transport} and C{channel} are unique; this handler
corresponds to exactly one L{Channel} on one L{Transport}.
@note: It is the responsibility of this method to exit if the
underlying L{Transport} is closed. This can be done by checking
L{Transport.is_active} or noticing an EOF
on the L{Channel}. If this method loops forever without checking
for this case, your python interpreter may refuse to exit because
this thread will still be running.
@param name: name of the requested subsystem.
@type name: str
@param transport: the server-mode L{Transport}.
@type transport: L{Transport}
@param channel: the channel associated with this subsystem request.
@type channel: L{Channel}
"""
pass
def finish_subsystem(self):
"""
Perform any cleanup at the end of a subsystem. The default
implementation just closes the channel.
@since: 1.1
"""
self.__channel.close()

View File

@ -1,185 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
import select
import socket
import struct
from paramiko.common import *
from paramiko import util
from paramiko.channel import Channel
from paramiko.message import Message
CMD_INIT, CMD_VERSION, CMD_OPEN, CMD_CLOSE, CMD_READ, CMD_WRITE, CMD_LSTAT, CMD_FSTAT, \
CMD_SETSTAT, CMD_FSETSTAT, CMD_OPENDIR, CMD_READDIR, CMD_REMOVE, CMD_MKDIR, \
CMD_RMDIR, CMD_REALPATH, CMD_STAT, CMD_RENAME, CMD_READLINK, CMD_SYMLINK \
= range(1, 21)
CMD_STATUS, CMD_HANDLE, CMD_DATA, CMD_NAME, CMD_ATTRS = range(101, 106)
CMD_EXTENDED, CMD_EXTENDED_REPLY = range(200, 202)
SFTP_OK = 0
SFTP_EOF, SFTP_NO_SUCH_FILE, SFTP_PERMISSION_DENIED, SFTP_FAILURE, SFTP_BAD_MESSAGE, \
SFTP_NO_CONNECTION, SFTP_CONNECTION_LOST, SFTP_OP_UNSUPPORTED = range(1, 9)
SFTP_DESC = [ 'Success',
'End of file',
'No such file',
'Permission denied',
'Failure',
'Bad message',
'No connection',
'Connection lost',
'Operation unsupported' ]
SFTP_FLAG_READ = 0x1
SFTP_FLAG_WRITE = 0x2
SFTP_FLAG_APPEND = 0x4
SFTP_FLAG_CREATE = 0x8
SFTP_FLAG_TRUNC = 0x10
SFTP_FLAG_EXCL = 0x20
_VERSION = 3
# for debugging
CMD_NAMES = {
CMD_INIT: 'init',
CMD_VERSION: 'version',
CMD_OPEN: 'open',
CMD_CLOSE: 'close',
CMD_READ: 'read',
CMD_WRITE: 'write',
CMD_LSTAT: 'lstat',
CMD_FSTAT: 'fstat',
CMD_SETSTAT: 'setstat',
CMD_FSETSTAT: 'fsetstat',
CMD_OPENDIR: 'opendir',
CMD_READDIR: 'readdir',
CMD_REMOVE: 'remove',
CMD_MKDIR: 'mkdir',
CMD_RMDIR: 'rmdir',
CMD_REALPATH: 'realpath',
CMD_STAT: 'stat',
CMD_RENAME: 'rename',
CMD_READLINK: 'readlink',
CMD_SYMLINK: 'symlink',
CMD_STATUS: 'status',
CMD_HANDLE: 'handle',
CMD_DATA: 'data',
CMD_NAME: 'name',
CMD_ATTRS: 'attrs',
CMD_EXTENDED: 'extended',
CMD_EXTENDED_REPLY: 'extended_reply'
}
class SFTPError (Exception):
pass
class BaseSFTP (object):
def __init__(self):
self.logger = util.get_logger('paramiko.sftp')
self.sock = None
self.ultra_debug = False
### internals...
def _send_version(self):
self._send_packet(CMD_INIT, struct.pack('>I', _VERSION))
t, data = self._read_packet()
if t != CMD_VERSION:
raise SFTPError('Incompatible sftp protocol')
version = struct.unpack('>I', data[:4])[0]
# if version != _VERSION:
# raise SFTPError('Incompatible sftp protocol')
return version
def _send_server_version(self):
# winscp will freak out if the server sends version info before the
# client finishes sending INIT.
t, data = self._read_packet()
if t != CMD_INIT:
raise SFTPError('Incompatible sftp protocol')
version = struct.unpack('>I', data[:4])[0]
# advertise that we support "check-file"
extension_pairs = [ 'check-file', 'md5,sha1' ]
msg = Message()
msg.add_int(_VERSION)
msg.add(*extension_pairs)
self._send_packet(CMD_VERSION, str(msg))
return version
def _log(self, level, msg, *args):
self.logger.log(level, msg, *args)
def _write_all(self, out):
while len(out) > 0:
n = self.sock.send(out)
if n <= 0:
raise EOFError()
if n == len(out):
return
out = out[n:]
return
def _read_all(self, n):
out = ''
while n > 0:
if isinstance(self.sock, socket.socket):
# sometimes sftp is used directly over a socket instead of
# through a paramiko channel. in this case, check periodically
# if the socket is closed. (for some reason, recv() won't ever
# return or raise an exception, but calling select on a closed
# socket will.)
while True:
read, write, err = select.select([ self.sock ], [], [], 0.1)
if len(read) > 0:
x = self.sock.recv(n)
break
else:
x = self.sock.recv(n)
if len(x) == 0:
raise EOFError()
out += x
n -= len(x)
return out
def _send_packet(self, t, packet):
#self._log(DEBUG2, 'write: %s (len=%d)' % (CMD_NAMES.get(t, '0x%02x' % t), len(packet)))
out = struct.pack('>I', len(packet) + 1) + chr(t) + packet
if self.ultra_debug:
self._log(DEBUG, util.format_binary(out, 'OUT: '))
self._write_all(out)
def _read_packet(self):
x = self._read_all(4)
# most sftp servers won't accept packets larger than about 32k, so
# anything with the high byte set (> 16MB) is just garbage.
if x[0] != '\x00':
raise SFTPError('Garbage packet received')
size = struct.unpack('>I', x)[0]
data = self._read_all(size)
if self.ultra_debug:
self._log(DEBUG, util.format_binary(data, 'IN: '));
if size > 0:
t = ord(data[0])
#self._log(DEBUG2, 'read: %s (len=%d)' % (CMD_NAMES.get(t), '0x%02x' % t, len(data)-1))
return t, data[1:]
return 0, ''

View File

@ -1,220 +0,0 @@
# Copyright (C) 2003-2006 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
import stat
import time
from paramiko.common import *
from paramiko.sftp import *
class SFTPAttributes (object):
"""
Representation of the attributes of a file (or proxied file) for SFTP in
client or server mode. It attemps to mirror the object returned by
C{os.stat} as closely as possible, so it may have the following fields,
with the same meanings as those returned by an C{os.stat} object:
- st_size
- st_uid
- st_gid
- st_mode
- st_atime
- st_mtime
Because SFTP allows flags to have other arbitrary named attributes, these
are stored in a dict named C{attr}. Occasionally, the filename is also
stored, in C{filename}.
"""
FLAG_SIZE = 1
FLAG_UIDGID = 2
FLAG_PERMISSIONS = 4
FLAG_AMTIME = 8
FLAG_EXTENDED = 0x80000000L
def __init__(self):
"""
Create a new (empty) SFTPAttributes object. All fields will be empty.
"""
self._flags = 0
self.st_size = None
self.st_uid = None
self.st_gid = None
self.st_mode = None
self.st_atime = None
self.st_mtime = None
self.attr = {}
def from_stat(cls, obj, filename=None):
"""
Create an SFTPAttributes object from an existing C{stat} object (an
object returned by C{os.stat}).
@param obj: an object returned by C{os.stat} (or equivalent).
@type obj: object
@param filename: the filename associated with this file.
@type filename: str
@return: new L{SFTPAttributes} object with the same attribute fields.
@rtype: L{SFTPAttributes}
"""
attr = cls()
attr.st_size = obj.st_size
attr.st_uid = obj.st_uid
attr.st_gid = obj.st_gid
attr.st_mode = obj.st_mode
attr.st_atime = obj.st_atime
attr.st_mtime = obj.st_mtime
if filename is not None:
attr.filename = filename
return attr
from_stat = classmethod(from_stat)
def __repr__(self):
return '<SFTPAttributes: %s>' % self._debug_str()
### internals...
def _from_msg(cls, msg, filename=None, longname=None):
attr = cls()
attr._unpack(msg)
if filename is not None:
attr.filename = filename
if longname is not None:
attr.longname = longname
return attr
_from_msg = classmethod(_from_msg)
def _unpack(self, msg):
self._flags = msg.get_int()
if self._flags & self.FLAG_SIZE:
self.st_size = msg.get_int64()
if self._flags & self.FLAG_UIDGID:
self.st_uid = msg.get_int()
self.st_gid = msg.get_int()
if self._flags & self.FLAG_PERMISSIONS:
self.st_mode = msg.get_int()
if self._flags & self.FLAG_AMTIME:
self.st_atime = msg.get_int()
self.st_mtime = msg.get_int()
if self._flags & self.FLAG_EXTENDED:
count = msg.get_int()
for i in range(count):
self.attr[msg.get_string()] = msg.get_string()
def _pack(self, msg):
self._flags = 0
if self.st_size is not None:
self._flags |= self.FLAG_SIZE
if (self.st_uid is not None) and (self.st_gid is not None):
self._flags |= self.FLAG_UIDGID
if self.st_mode is not None:
self._flags |= self.FLAG_PERMISSIONS
if (self.st_atime is not None) and (self.st_mtime is not None):
self._flags |= self.FLAG_AMTIME
if len(self.attr) > 0:
self._flags |= self.FLAG_EXTENDED
msg.add_int(self._flags)
if self._flags & self.FLAG_SIZE:
msg.add_int64(self.st_size)
if self._flags & self.FLAG_UIDGID:
msg.add_int(self.st_uid)
msg.add_int(self.st_gid)
if self._flags & self.FLAG_PERMISSIONS:
msg.add_int(self.st_mode)
if self._flags & self.FLAG_AMTIME:
# throw away any fractional seconds
msg.add_int(long(self.st_atime))
msg.add_int(long(self.st_mtime))
if self._flags & self.FLAG_EXTENDED:
msg.add_int(len(self.attr))
for key, val in self.attr.iteritems():
msg.add_string(key)
msg.add_string(val)
return
def _debug_str(self):
out = '[ '
if self.st_size is not None:
out += 'size=%d ' % self.st_size
if (self.st_uid is not None) and (self.st_gid is not None):
out += 'uid=%d gid=%d ' % (self.st_uid, self.st_gid)
if self.st_mode is not None:
out += 'mode=' + oct(self.st_mode) + ' '
if (self.st_atime is not None) and (self.st_mtime is not None):
out += 'atime=%d mtime=%d ' % (self.st_atime, self.st_mtime)
for k, v in self.attr.iteritems():
out += '"%s"=%r ' % (str(k), v)
out += ']'
return out
def _rwx(n, suid, sticky=False):
if suid:
suid = 2
out = '-r'[n >> 2] + '-w'[(n >> 1) & 1]
if sticky:
out += '-xTt'[suid + (n & 1)]
else:
out += '-xSs'[suid + (n & 1)]
return out
_rwx = staticmethod(_rwx)
def __str__(self):
"create a unix-style long description of the file (like ls -l)"
if self.st_mode is not None:
kind = stat.S_IFMT(self.st_mode)
if kind == stat.S_IFIFO:
ks = 'p'
elif kind == stat.S_IFCHR:
ks = 'c'
elif kind == stat.S_IFDIR:
ks = 'd'
elif kind == stat.S_IFBLK:
ks = 'b'
elif kind == stat.S_IFREG:
ks = '-'
elif kind == stat.S_IFLNK:
ks = 'l'
elif kind == stat.S_IFSOCK:
ks = 's'
else:
ks = '?'
ks += self._rwx((self.st_mode & 0700) >> 6, self.st_mode & stat.S_ISUID)
ks += self._rwx((self.st_mode & 070) >> 3, self.st_mode & stat.S_ISGID)
ks += self._rwx(self.st_mode & 7, self.st_mode & stat.S_ISVTX, True)
else:
ks = '?---------'
# compute display date
if (self.st_mtime is None) or (self.st_mtime == 0xffffffff):
# shouldn't really happen
datestr = '(unknown date)'
else:
if abs(time.time() - self.st_mtime) > 15552000:
# (15552000 = 6 months)
datestr = time.strftime('%d %b %Y', time.localtime(self.st_mtime))
else:
datestr = time.strftime('%d %b %H:%M', time.localtime(self.st_mtime))
filename = getattr(self, 'filename', '?')
# not all servers support uid/gid
uid = self.st_uid
gid = self.st_gid
if uid is None:
uid = 0
if gid is None:
gid = 0
return '%s 1 %-8d %-8d %8d %-12s %s' % (ks, uid, gid, self.st_size, datestr, filename)

View File

@ -1,723 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
Client-mode SFTP support.
"""
from binascii import hexlify
import errno
import os
import stat
import threading
import time
import weakref
from paramiko.sftp import *
from paramiko.sftp_attr import SFTPAttributes
from paramiko.ssh_exception import SSHException
from paramiko.sftp_file import SFTPFile
def _to_unicode(s):
"""
decode a string as ascii or utf8 if possible (as required by the sftp
protocol). if neither works, just return a byte string because the server
probably doesn't know the filename's encoding.
"""
try:
return s.encode('ascii')
except UnicodeError:
try:
return s.decode('utf-8')
except UnicodeError:
return s
class SFTPClient (BaseSFTP):
"""
SFTP client object. C{SFTPClient} is used to open an sftp session across
an open ssh L{Transport} and do remote file operations.
"""
def __init__(self, sock):
"""
Create an SFTP client from an existing L{Channel}. The channel
should already have requested the C{"sftp"} subsystem.
An alternate way to create an SFTP client context is by using
L{from_transport}.
@param sock: an open L{Channel} using the C{"sftp"} subsystem
@type sock: L{Channel}
@raise SSHException: if there's an exception while negotiating
sftp
"""
BaseSFTP.__init__(self)
self.sock = sock
self.ultra_debug = False
self.request_number = 1
# lock for request_number
self._lock = threading.Lock()
self._cwd = None
# request # -> SFTPFile
self._expecting = weakref.WeakValueDictionary()
if type(sock) is Channel:
# override default logger
transport = self.sock.get_transport()
self.logger = util.get_logger(transport.get_log_channel() + '.sftp')
self.ultra_debug = transport.get_hexdump()
try:
server_version = self._send_version()
except EOFError, x:
raise SSHException('EOF during negotiation')
self._log(INFO, 'Opened sftp connection (server version %d)' % server_version)
def from_transport(cls, t):
"""
Create an SFTP client channel from an open L{Transport}.
@param t: an open L{Transport} which is already authenticated
@type t: L{Transport}
@return: a new L{SFTPClient} object, referring to an sftp session
(channel) across the transport
@rtype: L{SFTPClient}
"""
chan = t.open_session()
if chan is None:
return None
chan.invoke_subsystem('sftp')
return cls(chan)
from_transport = classmethod(from_transport)
def _log(self, level, msg, *args):
if isinstance(msg, list):
for m in msg:
super(SFTPClient, self)._log(level, "[chan %s] " + m, *([ self.sock.get_name() ] + list(args)))
else:
super(SFTPClient, self)._log(level, "[chan %s] " + msg, *([ self.sock.get_name() ] + list(args)))
def close(self):
"""
Close the SFTP session and its underlying channel.
@since: 1.4
"""
self._log(INFO, 'sftp session closed.')
self.sock.close()
def get_channel(self):
"""
Return the underlying L{Channel} object for this SFTP session. This
might be useful for doing things like setting a timeout on the channel.
@return: the SSH channel
@rtype: L{Channel}
@since: 1.7.1
"""
return self.sock
def listdir(self, path='.'):
"""
Return a list containing the names of the entries in the given C{path}.
The list is in arbitrary order. It does not include the special
entries C{'.'} and C{'..'} even if they are present in the folder.
This method is meant to mirror C{os.listdir} as closely as possible.
For a list of full L{SFTPAttributes} objects, see L{listdir_attr}.
@param path: path to list (defaults to C{'.'})
@type path: str
@return: list of filenames
@rtype: list of str
"""
return [f.filename for f in self.listdir_attr(path)]
def listdir_attr(self, path='.'):
"""
Return a list containing L{SFTPAttributes} objects corresponding to
files in the given C{path}. The list is in arbitrary order. It does
not include the special entries C{'.'} and C{'..'} even if they are
present in the folder.
The returned L{SFTPAttributes} objects will each have an additional
field: C{longname}, which may contain a formatted string of the file's
attributes, in unix format. The content of this string will probably
depend on the SFTP server implementation.
@param path: path to list (defaults to C{'.'})
@type path: str
@return: list of attributes
@rtype: list of L{SFTPAttributes}
@since: 1.2
"""
path = self._adjust_cwd(path)
self._log(DEBUG, 'listdir(%r)' % path)
t, msg = self._request(CMD_OPENDIR, path)
if t != CMD_HANDLE:
raise SFTPError('Expected handle')
handle = msg.get_string()
filelist = []
while True:
try:
t, msg = self._request(CMD_READDIR, handle)
except EOFError, e:
# done with handle
break
if t != CMD_NAME:
raise SFTPError('Expected name response')
count = msg.get_int()
for i in range(count):
filename = _to_unicode(msg.get_string())
longname = _to_unicode(msg.get_string())
attr = SFTPAttributes._from_msg(msg, filename, longname)
if (filename != '.') and (filename != '..'):
filelist.append(attr)
self._request(CMD_CLOSE, handle)
return filelist
def open(self, filename, mode='r', bufsize=-1):
"""
Open a file on the remote server. The arguments are the same as for
python's built-in C{file} (aka C{open}). A file-like object is
returned, which closely mimics the behavior of a normal python file
object.
The mode indicates how the file is to be opened: C{'r'} for reading,
C{'w'} for writing (truncating an existing file), C{'a'} for appending,
C{'r+'} for reading/writing, C{'w+'} for reading/writing (truncating an
existing file), C{'a+'} for reading/appending. The python C{'b'} flag
is ignored, since SSH treats all files as binary. The C{'U'} flag is
supported in a compatible way.
Since 1.5.2, an C{'x'} flag indicates that the operation should only
succeed if the file was created and did not previously exist. This has
no direct mapping to python's file flags, but is commonly known as the
C{O_EXCL} flag in posix.
The file will be buffered in standard python style by default, but
can be altered with the C{bufsize} parameter. C{0} turns off
buffering, C{1} uses line buffering, and any number greater than 1
(C{>1}) uses that specific buffer size.
@param filename: name of the file to open
@type filename: str
@param mode: mode (python-style) to open in
@type mode: str
@param bufsize: desired buffering (-1 = default buffer size)
@type bufsize: int
@return: a file object representing the open file
@rtype: SFTPFile
@raise IOError: if the file could not be opened.
"""
filename = self._adjust_cwd(filename)
self._log(DEBUG, 'open(%r, %r)' % (filename, mode))
imode = 0
if ('r' in mode) or ('+' in mode):
imode |= SFTP_FLAG_READ
if ('w' in mode) or ('+' in mode) or ('a' in mode):
imode |= SFTP_FLAG_WRITE
if ('w' in mode):
imode |= SFTP_FLAG_CREATE | SFTP_FLAG_TRUNC
if ('a' in mode):
imode |= SFTP_FLAG_CREATE | SFTP_FLAG_APPEND
if ('x' in mode):
imode |= SFTP_FLAG_CREATE | SFTP_FLAG_EXCL
attrblock = SFTPAttributes()
t, msg = self._request(CMD_OPEN, filename, imode, attrblock)
if t != CMD_HANDLE:
raise SFTPError('Expected handle')
handle = msg.get_string()
self._log(DEBUG, 'open(%r, %r) -> %s' % (filename, mode, hexlify(handle)))
return SFTPFile(self, handle, mode, bufsize)
# python continues to vacillate about "open" vs "file"...
file = open
def remove(self, path):
"""
Remove the file at the given path. This only works on files; for
removing folders (directories), use L{rmdir}.
@param path: path (absolute or relative) of the file to remove
@type path: str
@raise IOError: if the path refers to a folder (directory)
"""
path = self._adjust_cwd(path)
self._log(DEBUG, 'remove(%r)' % path)
self._request(CMD_REMOVE, path)
unlink = remove
def rename(self, oldpath, newpath):
"""
Rename a file or folder from C{oldpath} to C{newpath}.
@param oldpath: existing name of the file or folder
@type oldpath: str
@param newpath: new name for the file or folder
@type newpath: str
@raise IOError: if C{newpath} is a folder, or something else goes
wrong
"""
oldpath = self._adjust_cwd(oldpath)
newpath = self._adjust_cwd(newpath)
self._log(DEBUG, 'rename(%r, %r)' % (oldpath, newpath))
self._request(CMD_RENAME, oldpath, newpath)
def mkdir(self, path, mode=0777):
"""
Create a folder (directory) named C{path} with numeric mode C{mode}.
The default mode is 0777 (octal). On some systems, mode is ignored.
Where it is used, the current umask value is first masked out.
@param path: name of the folder to create
@type path: str
@param mode: permissions (posix-style) for the newly-created folder
@type mode: int
"""
path = self._adjust_cwd(path)
self._log(DEBUG, 'mkdir(%r, %r)' % (path, mode))
attr = SFTPAttributes()
attr.st_mode = mode
self._request(CMD_MKDIR, path, attr)
def rmdir(self, path):
"""
Remove the folder named C{path}.
@param path: name of the folder to remove
@type path: str
"""
path = self._adjust_cwd(path)
self._log(DEBUG, 'rmdir(%r)' % path)
self._request(CMD_RMDIR, path)
def stat(self, path):
"""
Retrieve information about a file on the remote system. The return
value is an object whose attributes correspond to the attributes of
python's C{stat} structure as returned by C{os.stat}, except that it
contains fewer fields. An SFTP server may return as much or as little
info as it wants, so the results may vary from server to server.
Unlike a python C{stat} object, the result may not be accessed as a
tuple. This is mostly due to the author's slack factor.
The fields supported are: C{st_mode}, C{st_size}, C{st_uid}, C{st_gid},
C{st_atime}, and C{st_mtime}.
@param path: the filename to stat
@type path: str
@return: an object containing attributes about the given file
@rtype: SFTPAttributes
"""
path = self._adjust_cwd(path)
self._log(DEBUG, 'stat(%r)' % path)
t, msg = self._request(CMD_STAT, path)
if t != CMD_ATTRS:
raise SFTPError('Expected attributes')
return SFTPAttributes._from_msg(msg)
def lstat(self, path):
"""
Retrieve information about a file on the remote system, without
following symbolic links (shortcuts). This otherwise behaves exactly
the same as L{stat}.
@param path: the filename to stat
@type path: str
@return: an object containing attributes about the given file
@rtype: SFTPAttributes
"""
path = self._adjust_cwd(path)
self._log(DEBUG, 'lstat(%r)' % path)
t, msg = self._request(CMD_LSTAT, path)
if t != CMD_ATTRS:
raise SFTPError('Expected attributes')
return SFTPAttributes._from_msg(msg)
def symlink(self, source, dest):
"""
Create a symbolic link (shortcut) of the C{source} path at
C{destination}.
@param source: path of the original file
@type source: str
@param dest: path of the newly created symlink
@type dest: str
"""
dest = self._adjust_cwd(dest)
self._log(DEBUG, 'symlink(%r, %r)' % (source, dest))
if type(source) is unicode:
source = source.encode('utf-8')
self._request(CMD_SYMLINK, source, dest)
def chmod(self, path, mode):
"""
Change the mode (permissions) of a file. The permissions are
unix-style and identical to those used by python's C{os.chmod}
function.
@param path: path of the file to change the permissions of
@type path: str
@param mode: new permissions
@type mode: int
"""
path = self._adjust_cwd(path)
self._log(DEBUG, 'chmod(%r, %r)' % (path, mode))
attr = SFTPAttributes()
attr.st_mode = mode
self._request(CMD_SETSTAT, path, attr)
def chown(self, path, uid, gid):
"""
Change the owner (C{uid}) and group (C{gid}) of a file. As with
python's C{os.chown} function, you must pass both arguments, so if you
only want to change one, use L{stat} first to retrieve the current
owner and group.
@param path: path of the file to change the owner and group of
@type path: str
@param uid: new owner's uid
@type uid: int
@param gid: new group id
@type gid: int
"""
path = self._adjust_cwd(path)
self._log(DEBUG, 'chown(%r, %r, %r)' % (path, uid, gid))
attr = SFTPAttributes()
attr.st_uid, attr.st_gid = uid, gid
self._request(CMD_SETSTAT, path, attr)
def utime(self, path, times):
"""
Set the access and modified times of the file specified by C{path}. If
C{times} is C{None}, then the file's access and modified times are set
to the current time. Otherwise, C{times} must be a 2-tuple of numbers,
of the form C{(atime, mtime)}, which is used to set the access and
modified times, respectively. This bizarre API is mimicked from python
for the sake of consistency -- I apologize.
@param path: path of the file to modify
@type path: str
@param times: C{None} or a tuple of (access time, modified time) in
standard internet epoch time (seconds since 01 January 1970 GMT)
@type times: tuple(int)
"""
path = self._adjust_cwd(path)
if times is None:
times = (time.time(), time.time())
self._log(DEBUG, 'utime(%r, %r)' % (path, times))
attr = SFTPAttributes()
attr.st_atime, attr.st_mtime = times
self._request(CMD_SETSTAT, path, attr)
def truncate(self, path, size):
"""
Change the size of the file specified by C{path}. This usually extends
or shrinks the size of the file, just like the C{truncate()} method on
python file objects.
@param path: path of the file to modify
@type path: str
@param size: the new size of the file
@type size: int or long
"""
path = self._adjust_cwd(path)
self._log(DEBUG, 'truncate(%r, %r)' % (path, size))
attr = SFTPAttributes()
attr.st_size = size
self._request(CMD_SETSTAT, path, attr)
def readlink(self, path):
"""
Return the target of a symbolic link (shortcut). You can use
L{symlink} to create these. The result may be either an absolute or
relative pathname.
@param path: path of the symbolic link file
@type path: str
@return: target path
@rtype: str
"""
path = self._adjust_cwd(path)
self._log(DEBUG, 'readlink(%r)' % path)
t, msg = self._request(CMD_READLINK, path)
if t != CMD_NAME:
raise SFTPError('Expected name response')
count = msg.get_int()
if count == 0:
return None
if count != 1:
raise SFTPError('Readlink returned %d results' % count)
return _to_unicode(msg.get_string())
def normalize(self, path):
"""
Return the normalized path (on the server) of a given path. This
can be used to quickly resolve symbolic links or determine what the
server is considering to be the "current folder" (by passing C{'.'}
as C{path}).
@param path: path to be normalized
@type path: str
@return: normalized form of the given path
@rtype: str
@raise IOError: if the path can't be resolved on the server
"""
path = self._adjust_cwd(path)
self._log(DEBUG, 'normalize(%r)' % path)
t, msg = self._request(CMD_REALPATH, path)
if t != CMD_NAME:
raise SFTPError('Expected name response')
count = msg.get_int()
if count != 1:
raise SFTPError('Realpath returned %d results' % count)
return _to_unicode(msg.get_string())
def chdir(self, path):
"""
Change the "current directory" of this SFTP session. Since SFTP
doesn't really have the concept of a current working directory, this
is emulated by paramiko. Once you use this method to set a working
directory, all operations on this SFTPClient object will be relative
to that path. You can pass in C{None} to stop using a current working
directory.
@param path: new current working directory
@type path: str
@raise IOError: if the requested path doesn't exist on the server
@since: 1.4
"""
if path is None:
self._cwd = None
return
if not stat.S_ISDIR(self.stat(path).st_mode):
raise SFTPError(errno.ENOTDIR, "%s: %s" % (os.strerror(errno.ENOTDIR), path))
self._cwd = self.normalize(path).encode('utf-8')
def getcwd(self):
"""
Return the "current working directory" for this SFTP session, as
emulated by paramiko. If no directory has been set with L{chdir},
this method will return C{None}.
@return: the current working directory on the server, or C{None}
@rtype: str
@since: 1.4
"""
return self._cwd
def put(self, localpath, remotepath, callback=None):
"""
Copy a local file (C{localpath}) to the SFTP server as C{remotepath}.
Any exception raised by operations will be passed through. This
method is primarily provided as a convenience.
The SFTP operations use pipelining for speed.
@param localpath: the local file to copy
@type localpath: str
@param remotepath: the destination path on the SFTP server
@type remotepath: str
@param callback: optional callback function that accepts the bytes
transferred so far and the total bytes to be transferred
(since 1.7.4)
@type callback: function(int, int)
@return: an object containing attributes about the given file
(since 1.7.4)
@rtype: SFTPAttributes
@since: 1.4
"""
file_size = os.stat(localpath).st_size
fl = file(localpath, 'rb')
try:
fr = self.file(remotepath, 'wb')
fr.set_pipelined(True)
size = 0
try:
while True:
data = fl.read(32768)
if len(data) == 0:
break
fr.write(data)
size += len(data)
if callback is not None:
callback(size, file_size)
finally:
fr.close()
finally:
fl.close()
s = self.stat(remotepath)
if s.st_size != size:
raise IOError('size mismatch in put! %d != %d' % (s.st_size, size))
return s
def get(self, remotepath, localpath, callback=None):
"""
Copy a remote file (C{remotepath}) from the SFTP server to the local
host as C{localpath}. Any exception raised by operations will be
passed through. This method is primarily provided as a convenience.
@param remotepath: the remote file to copy
@type remotepath: str
@param localpath: the destination path on the local host
@type localpath: str
@param callback: optional callback function that accepts the bytes
transferred so far and the total bytes to be transferred
(since 1.7.4)
@type callback: function(int, int)
@since: 1.4
"""
fr = self.file(remotepath, 'rb')
file_size = self.stat(remotepath).st_size
fr.prefetch()
try:
fl = file(localpath, 'wb')
try:
size = 0
while True:
data = fr.read(32768)
if len(data) == 0:
break
fl.write(data)
size += len(data)
if callback is not None:
callback(size, file_size)
finally:
fl.close()
finally:
fr.close()
s = os.stat(localpath)
if s.st_size != size:
raise IOError('size mismatch in get! %d != %d' % (s.st_size, size))
### internals...
def _request(self, t, *arg):
num = self._async_request(type(None), t, *arg)
return self._read_response(num)
def _async_request(self, fileobj, t, *arg):
# this method may be called from other threads (prefetch)
self._lock.acquire()
try:
msg = Message()
msg.add_int(self.request_number)
for item in arg:
if type(item) is int:
msg.add_int(item)
elif type(item) is long:
msg.add_int64(item)
elif type(item) is str:
msg.add_string(item)
elif type(item) is SFTPAttributes:
item._pack(msg)
else:
raise Exception('unknown type for %r type %r' % (item, type(item)))
num = self.request_number
self._expecting[num] = fileobj
self._send_packet(t, str(msg))
self.request_number += 1
finally:
self._lock.release()
return num
def _read_response(self, waitfor=None):
while True:
try:
t, data = self._read_packet()
except EOFError, e:
raise SSHException('Server connection dropped: %s' % (str(e),))
msg = Message(data)
num = msg.get_int()
if num not in self._expecting:
# might be response for a file that was closed before responses came back
self._log(DEBUG, 'Unexpected response #%d' % (num,))
if waitfor is None:
# just doing a single check
break
continue
fileobj = self._expecting[num]
del self._expecting[num]
if num == waitfor:
# synchronous
if t == CMD_STATUS:
self._convert_status(msg)
return t, msg
if fileobj is not type(None):
fileobj._async_response(t, msg)
if waitfor is None:
# just doing a single check
break
return (None, None)
def _finish_responses(self, fileobj):
while fileobj in self._expecting.values():
self._read_response()
fileobj._check_exception()
def _convert_status(self, msg):
"""
Raises EOFError or IOError on error status; otherwise does nothing.
"""
code = msg.get_int()
text = msg.get_string()
if code == SFTP_OK:
return
elif code == SFTP_EOF:
raise EOFError(text)
elif code == SFTP_NO_SUCH_FILE:
# clever idea from john a. meinel: map the error codes to errno
raise IOError(errno.ENOENT, text)
elif code == SFTP_PERMISSION_DENIED:
raise IOError(errno.EACCES, text)
else:
raise IOError(text)
def _adjust_cwd(self, path):
"""
Return an adjusted path if we're emulating a "current working
directory" for the server.
"""
if type(path) is unicode:
path = path.encode('utf-8')
if self._cwd is None:
return path
if (len(path) > 0) and (path[0] == '/'):
# absolute path
return path
if self._cwd == '/':
return self._cwd + path
return self._cwd + '/' + path
class SFTP (SFTPClient):
"an alias for L{SFTPClient} for backwards compatability"
pass

View File

@ -1,473 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
L{SFTPFile}
"""
from binascii import hexlify
import socket
import threading
import time
from paramiko.common import *
from paramiko.sftp import *
from paramiko.file import BufferedFile
from paramiko.sftp_attr import SFTPAttributes
class SFTPFile (BufferedFile):
"""
Proxy object for a file on the remote server, in client mode SFTP.
"""
# Some sftp servers will choke if you send read/write requests larger than
# this size.
MAX_REQUEST_SIZE = 32768
def __init__(self, sftp, handle, mode='r', bufsize=-1):
BufferedFile.__init__(self)
self.sftp = sftp
self.handle = handle
BufferedFile._set_mode(self, mode, bufsize)
self.pipelined = False
self._prefetching = False
self._prefetch_done = False
self._prefetch_data = {}
self._prefetch_reads = []
self._saved_exception = None
def __del__(self):
self._close(async=True)
def close(self):
self._close(async=False)
def _close(self, async=False):
# We allow double-close without signaling an error, because real
# Python file objects do. However, we must protect against actually
# sending multiple CMD_CLOSE packets, because after we close our
# handle, the same handle may be re-allocated by the server, and we
# may end up mysteriously closing some random other file. (This is
# especially important because we unconditionally call close() from
# __del__.)
if self._closed:
return
self.sftp._log(DEBUG, 'close(%s)' % hexlify(self.handle))
if self.pipelined:
self.sftp._finish_responses(self)
BufferedFile.close(self)
try:
if async:
# GC'd file handle could be called from an arbitrary thread -- don't wait for a response
self.sftp._async_request(type(None), CMD_CLOSE, self.handle)
else:
self.sftp._request(CMD_CLOSE, self.handle)
except EOFError:
# may have outlived the Transport connection
pass
except (IOError, socket.error):
# may have outlived the Transport connection
pass
def _data_in_prefetch_requests(self, offset, size):
k = [i for i in self._prefetch_reads if i[0] <= offset]
if len(k) == 0:
return False
k.sort(lambda x, y: cmp(x[0], y[0]))
buf_offset, buf_size = k[-1]
if buf_offset + buf_size <= offset:
# prefetch request ends before this one begins
return False
if buf_offset + buf_size >= offset + size:
# inclusive
return True
# well, we have part of the request. see if another chunk has the rest.
return self._data_in_prefetch_requests(buf_offset + buf_size, offset + size - buf_offset - buf_size)
def _data_in_prefetch_buffers(self, offset):
"""
if a block of data is present in the prefetch buffers, at the given
offset, return the offset of the relevant prefetch buffer. otherwise,
return None. this guarantees nothing about the number of bytes
collected in the prefetch buffer so far.
"""
k = [i for i in self._prefetch_data.keys() if i <= offset]
if len(k) == 0:
return None
index = max(k)
buf_offset = offset - index
if buf_offset >= len(self._prefetch_data[index]):
# it's not here
return None
return index
def _read_prefetch(self, size):
"""
read data out of the prefetch buffer, if possible. if the data isn't
in the buffer, return None. otherwise, behaves like a normal read.
"""
# while not closed, and haven't fetched past the current position, and haven't reached EOF...
while True:
offset = self._data_in_prefetch_buffers(self._realpos)
if offset is not None:
break
if self._prefetch_done or self._closed:
break
self.sftp._read_response()
self._check_exception()
if offset is None:
self._prefetching = False
return None
prefetch = self._prefetch_data[offset]
del self._prefetch_data[offset]
buf_offset = self._realpos - offset
if buf_offset > 0:
self._prefetch_data[offset] = prefetch[:buf_offset]
prefetch = prefetch[buf_offset:]
if size < len(prefetch):
self._prefetch_data[self._realpos + size] = prefetch[size:]
prefetch = prefetch[:size]
return prefetch
def _read(self, size):
size = min(size, self.MAX_REQUEST_SIZE)
if self._prefetching:
data = self._read_prefetch(size)
if data is not None:
return data
t, msg = self.sftp._request(CMD_READ, self.handle, long(self._realpos), int(size))
if t != CMD_DATA:
raise SFTPError('Expected data')
return msg.get_string()
def _write(self, data):
# may write less than requested if it would exceed max packet size
chunk = min(len(data), self.MAX_REQUEST_SIZE)
req = self.sftp._async_request(type(None), CMD_WRITE, self.handle, long(self._realpos), str(data[:chunk]))
if not self.pipelined or self.sftp.sock.recv_ready():
t, msg = self.sftp._read_response(req)
if t != CMD_STATUS:
raise SFTPError('Expected status')
# convert_status already called
return chunk
def settimeout(self, timeout):
"""
Set a timeout on read/write operations on the underlying socket or
ssh L{Channel}.
@see: L{Channel.settimeout}
@param timeout: seconds to wait for a pending read/write operation
before raising C{socket.timeout}, or C{None} for no timeout
@type timeout: float
"""
self.sftp.sock.settimeout(timeout)
def gettimeout(self):
"""
Returns the timeout in seconds (as a float) associated with the socket
or ssh L{Channel} used for this file.
@see: L{Channel.gettimeout}
@rtype: float
"""
return self.sftp.sock.gettimeout()
def setblocking(self, blocking):
"""
Set blocking or non-blocking mode on the underiying socket or ssh
L{Channel}.
@see: L{Channel.setblocking}
@param blocking: 0 to set non-blocking mode; non-0 to set blocking
mode.
@type blocking: int
"""
self.sftp.sock.setblocking(blocking)
def seek(self, offset, whence=0):
self.flush()
if whence == self.SEEK_SET:
self._realpos = self._pos = offset
elif whence == self.SEEK_CUR:
self._pos += offset
self._realpos = self._pos
else:
self._realpos = self._pos = self._get_size() + offset
self._rbuffer = ''
def stat(self):
"""
Retrieve information about this file from the remote system. This is
exactly like L{SFTP.stat}, except that it operates on an already-open
file.
@return: an object containing attributes about this file.
@rtype: SFTPAttributes
"""
t, msg = self.sftp._request(CMD_FSTAT, self.handle)
if t != CMD_ATTRS:
raise SFTPError('Expected attributes')
return SFTPAttributes._from_msg(msg)
def chmod(self, mode):
"""
Change the mode (permissions) of this file. The permissions are
unix-style and identical to those used by python's C{os.chmod}
function.
@param mode: new permissions
@type mode: int
"""
self.sftp._log(DEBUG, 'chmod(%s, %r)' % (hexlify(self.handle), mode))
attr = SFTPAttributes()
attr.st_mode = mode
self.sftp._request(CMD_FSETSTAT, self.handle, attr)
def chown(self, uid, gid):
"""
Change the owner (C{uid}) and group (C{gid}) of this file. As with
python's C{os.chown} function, you must pass both arguments, so if you
only want to change one, use L{stat} first to retrieve the current
owner and group.
@param uid: new owner's uid
@type uid: int
@param gid: new group id
@type gid: int
"""
self.sftp._log(DEBUG, 'chown(%s, %r, %r)' % (hexlify(self.handle), uid, gid))
attr = SFTPAttributes()
attr.st_uid, attr.st_gid = uid, gid
self.sftp._request(CMD_FSETSTAT, self.handle, attr)
def utime(self, times):
"""
Set the access and modified times of this file. If
C{times} is C{None}, then the file's access and modified times are set
to the current time. Otherwise, C{times} must be a 2-tuple of numbers,
of the form C{(atime, mtime)}, which is used to set the access and
modified times, respectively. This bizarre API is mimicked from python
for the sake of consistency -- I apologize.
@param times: C{None} or a tuple of (access time, modified time) in
standard internet epoch time (seconds since 01 January 1970 GMT)
@type times: tuple(int)
"""
if times is None:
times = (time.time(), time.time())
self.sftp._log(DEBUG, 'utime(%s, %r)' % (hexlify(self.handle), times))
attr = SFTPAttributes()
attr.st_atime, attr.st_mtime = times
self.sftp._request(CMD_FSETSTAT, self.handle, attr)
def truncate(self, size):
"""
Change the size of this file. This usually extends
or shrinks the size of the file, just like the C{truncate()} method on
python file objects.
@param size: the new size of the file
@type size: int or long
"""
self.sftp._log(DEBUG, 'truncate(%s, %r)' % (hexlify(self.handle), size))
attr = SFTPAttributes()
attr.st_size = size
self.sftp._request(CMD_FSETSTAT, self.handle, attr)
def check(self, hash_algorithm, offset=0, length=0, block_size=0):
"""
Ask the server for a hash of a section of this file. This can be used
to verify a successful upload or download, or for various rsync-like
operations.
The file is hashed from C{offset}, for C{length} bytes. If C{length}
is 0, the remainder of the file is hashed. Thus, if both C{offset}
and C{length} are zero, the entire file is hashed.
Normally, C{block_size} will be 0 (the default), and this method will
return a byte string representing the requested hash (for example, a
string of length 16 for MD5, or 20 for SHA-1). If a non-zero
C{block_size} is given, each chunk of the file (from C{offset} to
C{offset + length}) of C{block_size} bytes is computed as a separate
hash. The hash results are all concatenated and returned as a single
string.
For example, C{check('sha1', 0, 1024, 512)} will return a string of
length 40. The first 20 bytes will be the SHA-1 of the first 512 bytes
of the file, and the last 20 bytes will be the SHA-1 of the next 512
bytes.
@param hash_algorithm: the name of the hash algorithm to use (normally
C{"sha1"} or C{"md5"})
@type hash_algorithm: str
@param offset: offset into the file to begin hashing (0 means to start
from the beginning)
@type offset: int or long
@param length: number of bytes to hash (0 means continue to the end of
the file)
@type length: int or long
@param block_size: number of bytes to hash per result (must not be less
than 256; 0 means to compute only one hash of the entire segment)
@type block_size: int
@return: string of bytes representing the hash of each block,
concatenated together
@rtype: str
@note: Many (most?) servers don't support this extension yet.
@raise IOError: if the server doesn't support the "check-file"
extension, or possibly doesn't support the hash algorithm
requested
@since: 1.4
"""
t, msg = self.sftp._request(CMD_EXTENDED, 'check-file', self.handle,
hash_algorithm, long(offset), long(length), block_size)
ext = msg.get_string()
alg = msg.get_string()
data = msg.get_remainder()
return data
def set_pipelined(self, pipelined=True):
"""
Turn on/off the pipelining of write operations to this file. When
pipelining is on, paramiko won't wait for the server response after
each write operation. Instead, they're collected as they come in.
At the first non-write operation (including L{close}), all remaining
server responses are collected. This means that if there was an error
with one of your later writes, an exception might be thrown from
within L{close} instead of L{write}.
By default, files are I{not} pipelined.
@param pipelined: C{True} if pipelining should be turned on for this
file; C{False} otherwise
@type pipelined: bool
@since: 1.5
"""
self.pipelined = pipelined
def prefetch(self):
"""
Pre-fetch the remaining contents of this file in anticipation of
future L{read} calls. If reading the entire file, pre-fetching can
dramatically improve the download speed by avoiding roundtrip latency.
The file's contents are incrementally buffered in a background thread.
The prefetched data is stored in a buffer until read via the L{read}
method. Once data has been read, it's removed from the buffer. The
data may be read in a random order (using L{seek}); chunks of the
buffer that haven't been read will continue to be buffered.
@since: 1.5.1
"""
size = self.stat().st_size
# queue up async reads for the rest of the file
chunks = []
n = self._realpos
while n < size:
chunk = min(self.MAX_REQUEST_SIZE, size - n)
chunks.append((n, chunk))
n += chunk
if len(chunks) > 0:
self._start_prefetch(chunks)
def readv(self, chunks):
"""
Read a set of blocks from the file by (offset, length). This is more
efficient than doing a series of L{seek} and L{read} calls, since the
prefetch machinery is used to retrieve all the requested blocks at
once.
@param chunks: a list of (offset, length) tuples indicating which
sections of the file to read
@type chunks: list(tuple(long, int))
@return: a list of blocks read, in the same order as in C{chunks}
@rtype: list(str)
@since: 1.5.4
"""
self.sftp._log(DEBUG, 'readv(%s, %r)' % (hexlify(self.handle), chunks))
read_chunks = []
for offset, size in chunks:
# don't fetch data that's already in the prefetch buffer
if self._data_in_prefetch_buffers(offset) or self._data_in_prefetch_requests(offset, size):
continue
# break up anything larger than the max read size
while size > 0:
chunk_size = min(size, self.MAX_REQUEST_SIZE)
read_chunks.append((offset, chunk_size))
offset += chunk_size
size -= chunk_size
self._start_prefetch(read_chunks)
# now we can just devolve to a bunch of read()s :)
for x in chunks:
self.seek(x[0])
yield self.read(x[1])
### internals...
def _get_size(self):
try:
return self.stat().st_size
except:
return 0
def _start_prefetch(self, chunks):
self._prefetching = True
self._prefetch_done = False
self._prefetch_reads.extend(chunks)
t = threading.Thread(target=self._prefetch_thread, args=(chunks,))
t.setDaemon(True)
t.start()
def _prefetch_thread(self, chunks):
# do these read requests in a temporary thread because there may be
# a lot of them, so it may block.
for offset, length in chunks:
self.sftp._async_request(self, CMD_READ, self.handle, long(offset), int(length))
def _async_response(self, t, msg):
if t == CMD_STATUS:
# save exception and re-raise it on next file operation
try:
self.sftp._convert_status(msg)
except Exception, x:
self._saved_exception = x
return
if t != CMD_DATA:
raise SFTPError('Expected data')
data = msg.get_string()
offset, length = self._prefetch_reads.pop(0)
self._prefetch_data[offset] = data
if len(self._prefetch_reads) == 0:
self._prefetch_done = True
def _check_exception(self):
"if there's a saved exception, raise & clear it"
if self._saved_exception is not None:
x = self._saved_exception
self._saved_exception = None
raise x

View File

@ -1,199 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
Abstraction of an SFTP file handle (for server mode).
"""
import os
from paramiko.common import *
from paramiko.sftp import *
class SFTPHandle (object):
"""
Abstract object representing a handle to an open file (or folder) in an
SFTP server implementation. Each handle has a string representation used
by the client to refer to the underlying file.
Server implementations can (and should) subclass SFTPHandle to implement
features of a file handle, like L{stat} or L{chattr}.
"""
def __init__(self, flags=0):
"""
Create a new file handle representing a local file being served over
SFTP. If C{flags} is passed in, it's used to determine if the file
is open in append mode.
@param flags: optional flags as passed to L{SFTPServerInterface.open}
@type flags: int
"""
self.__flags = flags
self.__name = None
# only for handles to folders:
self.__files = { }
self.__tell = None
def close(self):
"""
When a client closes a file, this method is called on the handle.
Normally you would use this method to close the underlying OS level
file object(s).
The default implementation checks for attributes on C{self} named
C{readfile} and/or C{writefile}, and if either or both are present,
their C{close()} methods are called. This means that if you are
using the default implementations of L{read} and L{write}, this
method's default implementation should be fine also.
"""
readfile = getattr(self, 'readfile', None)
if readfile is not None:
readfile.close()
writefile = getattr(self, 'writefile', None)
if writefile is not None:
writefile.close()
def read(self, offset, length):
"""
Read up to C{length} bytes from this file, starting at position
C{offset}. The offset may be a python long, since SFTP allows it
to be 64 bits.
If the end of the file has been reached, this method may return an
empty string to signify EOF, or it may also return L{SFTP_EOF}.
The default implementation checks for an attribute on C{self} named
C{readfile}, and if present, performs the read operation on the python
file-like object found there. (This is meant as a time saver for the
common case where you are wrapping a python file object.)
@param offset: position in the file to start reading from.
@type offset: int or long
@param length: number of bytes to attempt to read.
@type length: int
@return: data read from the file, or an SFTP error code.
@rtype: str
"""
readfile = getattr(self, 'readfile', None)
if readfile is None:
return SFTP_OP_UNSUPPORTED
try:
if self.__tell is None:
self.__tell = readfile.tell()
if offset != self.__tell:
readfile.seek(offset)
self.__tell = offset
data = readfile.read(length)
except IOError, e:
self.__tell = None
return SFTPServer.convert_errno(e.errno)
self.__tell += len(data)
return data
def write(self, offset, data):
"""
Write C{data} into this file at position C{offset}. Extending the
file past its original end is expected. Unlike python's normal
C{write()} methods, this method cannot do a partial write: it must
write all of C{data} or else return an error.
The default implementation checks for an attribute on C{self} named
C{writefile}, and if present, performs the write operation on the
python file-like object found there. The attribute is named
differently from C{readfile} to make it easy to implement read-only
(or write-only) files, but if both attributes are present, they should
refer to the same file.
@param offset: position in the file to start reading from.
@type offset: int or long
@param data: data to write into the file.
@type data: str
@return: an SFTP error code like L{SFTP_OK}.
"""
writefile = getattr(self, 'writefile', None)
if writefile is None:
return SFTP_OP_UNSUPPORTED
try:
# in append mode, don't care about seeking
if (self.__flags & os.O_APPEND) == 0:
if self.__tell is None:
self.__tell = writefile.tell()
if offset != self.__tell:
writefile.seek(offset)
self.__tell = offset
writefile.write(data)
writefile.flush()
except IOError, e:
self.__tell = None
return SFTPServer.convert_errno(e.errno)
if self.__tell is not None:
self.__tell += len(data)
return SFTP_OK
def stat(self):
"""
Return an L{SFTPAttributes} object referring to this open file, or an
error code. This is equivalent to L{SFTPServerInterface.stat}, except
it's called on an open file instead of a path.
@return: an attributes object for the given file, or an SFTP error
code (like L{SFTP_PERMISSION_DENIED}).
@rtype: L{SFTPAttributes} I{or error code}
"""
return SFTP_OP_UNSUPPORTED
def chattr(self, attr):
"""
Change the attributes of this file. The C{attr} object will contain
only those fields provided by the client in its request, so you should
check for the presence of fields before using them.
@param attr: the attributes to change on this file.
@type attr: L{SFTPAttributes}
@return: an error code like L{SFTP_OK}.
@rtype: int
"""
return SFTP_OP_UNSUPPORTED
### internals...
def _set_files(self, files):
"""
Used by the SFTP server code to cache a directory listing. (In
the SFTP protocol, listing a directory is a multi-stage process
requiring a temporary handle.)
"""
self.__files = files
def _get_next_files(self):
"""
Used by the SFTP server code to retreive a cached directory
listing.
"""
fnlist = self.__files[:16]
self.__files = self.__files[16:]
return fnlist
def _get_name(self):
return self.__name
def _set_name(self, name):
self.__name = name
from paramiko.sftp_server import SFTPServer

View File

@ -1,441 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
Server-mode SFTP support.
"""
import os
import errno
from Crypto.Hash import MD5, SHA
from paramiko.common import *
from paramiko.server import SubsystemHandler
from paramiko.sftp import *
from paramiko.sftp_si import *
from paramiko.sftp_attr import *
# known hash algorithms for the "check-file" extension
_hash_class = {
'sha1': SHA,
'md5': MD5,
}
class SFTPServer (BaseSFTP, SubsystemHandler):
"""
Server-side SFTP subsystem support. Since this is a L{SubsystemHandler},
it can be (and is meant to be) set as the handler for C{"sftp"} requests.
Use L{Transport.set_subsystem_handler} to activate this class.
"""
def __init__(self, channel, name, server, sftp_si=SFTPServerInterface, *largs, **kwargs):
"""
The constructor for SFTPServer is meant to be called from within the
L{Transport} as a subsystem handler. C{server} and any additional
parameters or keyword parameters are passed from the original call to
L{Transport.set_subsystem_handler}.
@param channel: channel passed from the L{Transport}.
@type channel: L{Channel}
@param name: name of the requested subsystem.
@type name: str
@param server: the server object associated with this channel and
subsystem
@type server: L{ServerInterface}
@param sftp_si: a subclass of L{SFTPServerInterface} to use for handling
individual requests.
@type sftp_si: class
"""
BaseSFTP.__init__(self)
SubsystemHandler.__init__(self, channel, name, server)
transport = channel.get_transport()
self.logger = util.get_logger(transport.get_log_channel() + '.sftp')
self.ultra_debug = transport.get_hexdump()
self.next_handle = 1
# map of handle-string to SFTPHandle for files & folders:
self.file_table = { }
self.folder_table = { }
self.server = sftp_si(server, *largs, **kwargs)
def _log(self, level, msg):
if issubclass(type(msg), list):
for m in msg:
super(SFTPServer, self)._log(level, "[chan " + self.sock.get_name() + "] " + m)
else:
super(SFTPServer, self)._log(level, "[chan " + self.sock.get_name() + "] " + msg)
def start_subsystem(self, name, transport, channel):
self.sock = channel
self._log(DEBUG, 'Started sftp server on channel %s' % repr(channel))
self._send_server_version()
self.server.session_started()
while True:
try:
t, data = self._read_packet()
except EOFError:
self._log(DEBUG, 'EOF -- end of session')
return
except Exception, e:
self._log(DEBUG, 'Exception on channel: ' + str(e))
self._log(DEBUG, util.tb_strings())
return
msg = Message(data)
request_number = msg.get_int()
try:
self._process(t, request_number, msg)
except Exception, e:
self._log(DEBUG, 'Exception in server processing: ' + str(e))
self._log(DEBUG, util.tb_strings())
# send some kind of failure message, at least
try:
self._send_status(request_number, SFTP_FAILURE)
except:
pass
def finish_subsystem(self):
self.server.session_ended()
super(SFTPServer, self).finish_subsystem()
# close any file handles that were left open (so we can return them to the OS quickly)
for f in self.file_table.itervalues():
f.close()
for f in self.folder_table.itervalues():
f.close()
self.file_table = {}
self.folder_table = {}
def convert_errno(e):
"""
Convert an errno value (as from an C{OSError} or C{IOError}) into a
standard SFTP result code. This is a convenience function for trapping
exceptions in server code and returning an appropriate result.
@param e: an errno code, as from C{OSError.errno}.
@type e: int
@return: an SFTP error code like L{SFTP_NO_SUCH_FILE}.
@rtype: int
"""
if e == errno.EACCES:
# permission denied
return SFTP_PERMISSION_DENIED
elif (e == errno.ENOENT) or (e == errno.ENOTDIR):
# no such file
return SFTP_NO_SUCH_FILE
else:
return SFTP_FAILURE
convert_errno = staticmethod(convert_errno)
def set_file_attr(filename, attr):
"""
Change a file's attributes on the local filesystem. The contents of
C{attr} are used to change the permissions, owner, group ownership,
and/or modification & access time of the file, depending on which
attributes are present in C{attr}.
This is meant to be a handy helper function for translating SFTP file
requests into local file operations.
@param filename: name of the file to alter (should usually be an
absolute path).
@type filename: str
@param attr: attributes to change.
@type attr: L{SFTPAttributes}
"""
if sys.platform != 'win32':
# mode operations are meaningless on win32
if attr._flags & attr.FLAG_PERMISSIONS:
os.chmod(filename, attr.st_mode)
if attr._flags & attr.FLAG_UIDGID:
os.chown(filename, attr.st_uid, attr.st_gid)
if attr._flags & attr.FLAG_AMTIME:
os.utime(filename, (attr.st_atime, attr.st_mtime))
if attr._flags & attr.FLAG_SIZE:
open(filename, 'w+').truncate(attr.st_size)
set_file_attr = staticmethod(set_file_attr)
### internals...
def _response(self, request_number, t, *arg):
msg = Message()
msg.add_int(request_number)
for item in arg:
if type(item) is int:
msg.add_int(item)
elif type(item) is long:
msg.add_int64(item)
elif type(item) is str:
msg.add_string(item)
elif type(item) is SFTPAttributes:
item._pack(msg)
else:
raise Exception('unknown type for ' + repr(item) + ' type ' + repr(type(item)))
self._send_packet(t, str(msg))
def _send_handle_response(self, request_number, handle, folder=False):
if not issubclass(type(handle), SFTPHandle):
# must be error code
self._send_status(request_number, handle)
return
handle._set_name('hx%d' % self.next_handle)
self.next_handle += 1
if folder:
self.folder_table[handle._get_name()] = handle
else:
self.file_table[handle._get_name()] = handle
self._response(request_number, CMD_HANDLE, handle._get_name())
def _send_status(self, request_number, code, desc=None):
if desc is None:
try:
desc = SFTP_DESC[code]
except IndexError:
desc = 'Unknown'
# some clients expect a "langauge" tag at the end (but don't mind it being blank)
self._response(request_number, CMD_STATUS, code, desc, '')
def _open_folder(self, request_number, path):
resp = self.server.list_folder(path)
if issubclass(type(resp), list):
# got an actual list of filenames in the folder
folder = SFTPHandle()
folder._set_files(resp)
self._send_handle_response(request_number, folder, True)
return
# must be an error code
self._send_status(request_number, resp)
def _read_folder(self, request_number, folder):
flist = folder._get_next_files()
if len(flist) == 0:
self._send_status(request_number, SFTP_EOF)
return
msg = Message()
msg.add_int(request_number)
msg.add_int(len(flist))
for attr in flist:
msg.add_string(attr.filename)
msg.add_string(str(attr))
attr._pack(msg)
self._send_packet(CMD_NAME, str(msg))
def _check_file(self, request_number, msg):
# this extension actually comes from v6 protocol, but since it's an
# extension, i feel like we can reasonably support it backported.
# it's very useful for verifying uploaded files or checking for
# rsync-like differences between local and remote files.
handle = msg.get_string()
alg_list = msg.get_list()
start = msg.get_int64()
length = msg.get_int64()
block_size = msg.get_int()
if handle not in self.file_table:
self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle')
return
f = self.file_table[handle]
for x in alg_list:
if x in _hash_class:
algname = x
alg = _hash_class[x]
break
else:
self._send_status(request_number, SFTP_FAILURE, 'No supported hash types found')
return
if length == 0:
st = f.stat()
if not issubclass(type(st), SFTPAttributes):
self._send_status(request_number, st, 'Unable to stat file')
return
length = st.st_size - start
if block_size == 0:
block_size = length
if block_size < 256:
self._send_status(request_number, SFTP_FAILURE, 'Block size too small')
return
sum_out = ''
offset = start
while offset < start + length:
blocklen = min(block_size, start + length - offset)
# don't try to read more than about 64KB at a time
chunklen = min(blocklen, 65536)
count = 0
hash_obj = alg.new()
while count < blocklen:
data = f.read(offset, chunklen)
if not type(data) is str:
self._send_status(request_number, data, 'Unable to hash file')
return
hash_obj.update(data)
count += len(data)
offset += count
sum_out += hash_obj.digest()
msg = Message()
msg.add_int(request_number)
msg.add_string('check-file')
msg.add_string(algname)
msg.add_bytes(sum_out)
self._send_packet(CMD_EXTENDED_REPLY, str(msg))
def _convert_pflags(self, pflags):
"convert SFTP-style open() flags to python's os.open() flags"
if (pflags & SFTP_FLAG_READ) and (pflags & SFTP_FLAG_WRITE):
flags = os.O_RDWR
elif pflags & SFTP_FLAG_WRITE:
flags = os.O_WRONLY
else:
flags = os.O_RDONLY
if pflags & SFTP_FLAG_APPEND:
flags |= os.O_APPEND
if pflags & SFTP_FLAG_CREATE:
flags |= os.O_CREAT
if pflags & SFTP_FLAG_TRUNC:
flags |= os.O_TRUNC
if pflags & SFTP_FLAG_EXCL:
flags |= os.O_EXCL
return flags
def _process(self, t, request_number, msg):
self._log(DEBUG, 'Request: %s' % CMD_NAMES[t])
if t == CMD_OPEN:
path = msg.get_string()
flags = self._convert_pflags(msg.get_int())
attr = SFTPAttributes._from_msg(msg)
self._send_handle_response(request_number, self.server.open(path, flags, attr))
elif t == CMD_CLOSE:
handle = msg.get_string()
if handle in self.folder_table:
del self.folder_table[handle]
self._send_status(request_number, SFTP_OK)
return
if handle in self.file_table:
self.file_table[handle].close()
del self.file_table[handle]
self._send_status(request_number, SFTP_OK)
return
self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle')
elif t == CMD_READ:
handle = msg.get_string()
offset = msg.get_int64()
length = msg.get_int()
if handle not in self.file_table:
self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle')
return
data = self.file_table[handle].read(offset, length)
if type(data) is str:
if len(data) == 0:
self._send_status(request_number, SFTP_EOF)
else:
self._response(request_number, CMD_DATA, data)
else:
self._send_status(request_number, data)
elif t == CMD_WRITE:
handle = msg.get_string()
offset = msg.get_int64()
data = msg.get_string()
if handle not in self.file_table:
self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle')
return
self._send_status(request_number, self.file_table[handle].write(offset, data))
elif t == CMD_REMOVE:
path = msg.get_string()
self._send_status(request_number, self.server.remove(path))
elif t == CMD_RENAME:
oldpath = msg.get_string()
newpath = msg.get_string()
self._send_status(request_number, self.server.rename(oldpath, newpath))
elif t == CMD_MKDIR:
path = msg.get_string()
attr = SFTPAttributes._from_msg(msg)
self._send_status(request_number, self.server.mkdir(path, attr))
elif t == CMD_RMDIR:
path = msg.get_string()
self._send_status(request_number, self.server.rmdir(path))
elif t == CMD_OPENDIR:
path = msg.get_string()
self._open_folder(request_number, path)
return
elif t == CMD_READDIR:
handle = msg.get_string()
if handle not in self.folder_table:
self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle')
return
folder = self.folder_table[handle]
self._read_folder(request_number, folder)
elif t == CMD_STAT:
path = msg.get_string()
resp = self.server.stat(path)
if issubclass(type(resp), SFTPAttributes):
self._response(request_number, CMD_ATTRS, resp)
else:
self._send_status(request_number, resp)
elif t == CMD_LSTAT:
path = msg.get_string()
resp = self.server.lstat(path)
if issubclass(type(resp), SFTPAttributes):
self._response(request_number, CMD_ATTRS, resp)
else:
self._send_status(request_number, resp)
elif t == CMD_FSTAT:
handle = msg.get_string()
if handle not in self.file_table:
self._send_status(request_number, SFTP_BAD_MESSAGE, 'Invalid handle')
return
resp = self.file_table[handle].stat()
if issubclass(type(resp), SFTPAttributes):
self._response(request_number, CMD_ATTRS, resp)
else:
self._send_status(request_number, resp)
elif t == CMD_SETSTAT:
path = msg.get_string()
attr = SFTPAttributes._from_msg(msg)
self._send_status(request_number, self.server.chattr(path, attr))
elif t == CMD_FSETSTAT:
handle = msg.get_string()
attr = SFTPAttributes._from_msg(msg)
if handle not in self.file_table:
self._response(request_number, SFTP_BAD_MESSAGE, 'Invalid handle')
return
self._send_status(request_number, self.file_table[handle].chattr(attr))
elif t == CMD_READLINK:
path = msg.get_string()
resp = self.server.readlink(path)
if type(resp) is str:
self._response(request_number, CMD_NAME, 1, resp, '', SFTPAttributes())
else:
self._send_status(request_number, resp)
elif t == CMD_SYMLINK:
# the sftp 2 draft is incorrect here! path always follows target_path
target_path = msg.get_string()
path = msg.get_string()
self._send_status(request_number, self.server.symlink(target_path, path))
elif t == CMD_REALPATH:
path = msg.get_string()
rpath = self.server.canonicalize(path)
self._response(request_number, CMD_NAME, 1, rpath, '', SFTPAttributes())
elif t == CMD_EXTENDED:
tag = msg.get_string()
if tag == 'check-file':
self._check_file(request_number, msg)
else:
self._send_status(request_number, SFTP_OP_UNSUPPORTED)
else:
self._send_status(request_number, SFTP_OP_UNSUPPORTED)
from paramiko.sftp_handle import SFTPHandle

View File

@ -1,307 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
L{SFTPServerInterface} is an interface to override for SFTP server support.
"""
import os
from paramiko.common import *
from paramiko.sftp import *
class SFTPServerInterface (object):
"""
This class defines an interface for controlling the behavior of paramiko
when using the L{SFTPServer} subsystem to provide an SFTP server.
Methods on this class are called from the SFTP session's thread, so you can
block as long as necessary without affecting other sessions (even other
SFTP sessions). However, raising an exception will usually cause the SFTP
session to abruptly end, so you will usually want to catch exceptions and
return an appropriate error code.
All paths are in string form instead of unicode because not all SFTP
clients & servers obey the requirement that paths be encoded in UTF-8.
"""
def __init__ (self, server, *largs, **kwargs):
"""
Create a new SFTPServerInterface object. This method does nothing by
default and is meant to be overridden by subclasses.
@param server: the server object associated with this channel and
SFTP subsystem
@type server: L{ServerInterface}
"""
super(SFTPServerInterface, self).__init__(*largs, **kwargs)
def session_started(self):
"""
The SFTP server session has just started. This method is meant to be
overridden to perform any necessary setup before handling callbacks
from SFTP operations.
"""
pass
def session_ended(self):
"""
The SFTP server session has just ended, either cleanly or via an
exception. This method is meant to be overridden to perform any
necessary cleanup before this C{SFTPServerInterface} object is
destroyed.
"""
pass
def open(self, path, flags, attr):
"""
Open a file on the server and create a handle for future operations
on that file. On success, a new object subclassed from L{SFTPHandle}
should be returned. This handle will be used for future operations
on the file (read, write, etc). On failure, an error code such as
L{SFTP_PERMISSION_DENIED} should be returned.
C{flags} contains the requested mode for opening (read-only,
write-append, etc) as a bitset of flags from the C{os} module:
- C{os.O_RDONLY}
- C{os.O_WRONLY}
- C{os.O_RDWR}
- C{os.O_APPEND}
- C{os.O_CREAT}
- C{os.O_TRUNC}
- C{os.O_EXCL}
(One of C{os.O_RDONLY}, C{os.O_WRONLY}, or C{os.O_RDWR} will always
be set.)
The C{attr} object contains requested attributes of the file if it
has to be created. Some or all attribute fields may be missing if
the client didn't specify them.
@note: The SFTP protocol defines all files to be in "binary" mode.
There is no equivalent to python's "text" mode.
@param path: the requested path (relative or absolute) of the file
to be opened.
@type path: str
@param flags: flags or'd together from the C{os} module indicating the
requested mode for opening the file.
@type flags: int
@param attr: requested attributes of the file if it is newly created.
@type attr: L{SFTPAttributes}
@return: a new L{SFTPHandle} I{or error code}.
@rtype L{SFTPHandle}
"""
return SFTP_OP_UNSUPPORTED
def list_folder(self, path):
"""
Return a list of files within a given folder. The C{path} will use
posix notation (C{"/"} separates folder names) and may be an absolute
or relative path.
The list of files is expected to be a list of L{SFTPAttributes}
objects, which are similar in structure to the objects returned by
C{os.stat}. In addition, each object should have its C{filename}
field filled in, since this is important to a directory listing and
not normally present in C{os.stat} results. The method
L{SFTPAttributes.from_stat} will usually do what you want.
In case of an error, you should return one of the C{SFTP_*} error
codes, such as L{SFTP_PERMISSION_DENIED}.
@param path: the requested path (relative or absolute) to be listed.
@type path: str
@return: a list of the files in the given folder, using
L{SFTPAttributes} objects.
@rtype: list of L{SFTPAttributes} I{or error code}
@note: You should normalize the given C{path} first (see the
C{os.path} module) and check appropriate permissions before returning
the list of files. Be careful of malicious clients attempting to use
relative paths to escape restricted folders, if you're doing a direct
translation from the SFTP server path to your local filesystem.
"""
return SFTP_OP_UNSUPPORTED
def stat(self, path):
"""
Return an L{SFTPAttributes} object for a path on the server, or an
error code. If your server supports symbolic links (also known as
"aliases"), you should follow them. (L{lstat} is the corresponding
call that doesn't follow symlinks/aliases.)
@param path: the requested path (relative or absolute) to fetch
file statistics for.
@type path: str
@return: an attributes object for the given file, or an SFTP error
code (like L{SFTP_PERMISSION_DENIED}).
@rtype: L{SFTPAttributes} I{or error code}
"""
return SFTP_OP_UNSUPPORTED
def lstat(self, path):
"""
Return an L{SFTPAttributes} object for a path on the server, or an
error code. If your server supports symbolic links (also known as
"aliases"), you should I{not} follow them -- instead, you should
return data on the symlink or alias itself. (L{stat} is the
corresponding call that follows symlinks/aliases.)
@param path: the requested path (relative or absolute) to fetch
file statistics for.
@type path: str
@return: an attributes object for the given file, or an SFTP error
code (like L{SFTP_PERMISSION_DENIED}).
@rtype: L{SFTPAttributes} I{or error code}
"""
return SFTP_OP_UNSUPPORTED
def remove(self, path):
"""
Delete a file, if possible.
@param path: the requested path (relative or absolute) of the file
to delete.
@type path: str
@return: an SFTP error code like L{SFTP_OK}.
@rtype: int
"""
return SFTP_OP_UNSUPPORTED
def rename(self, oldpath, newpath):
"""
Rename (or move) a file. The SFTP specification implies that this
method can be used to move an existing file into a different folder,
and since there's no other (easy) way to move files via SFTP, it's
probably a good idea to implement "move" in this method too, even for
files that cross disk partition boundaries, if at all possible.
@note: You should return an error if a file with the same name as
C{newpath} already exists. (The rename operation should be
non-desctructive.)
@param oldpath: the requested path (relative or absolute) of the
existing file.
@type oldpath: str
@param newpath: the requested new path of the file.
@type newpath: str
@return: an SFTP error code like L{SFTP_OK}.
@rtype: int
"""
return SFTP_OP_UNSUPPORTED
def mkdir(self, path, attr):
"""
Create a new directory with the given attributes. The C{attr}
object may be considered a "hint" and ignored.
The C{attr} object will contain only those fields provided by the
client in its request, so you should use C{hasattr} to check for
the presense of fields before using them. In some cases, the C{attr}
object may be completely empty.
@param path: requested path (relative or absolute) of the new
folder.
@type path: str
@param attr: requested attributes of the new folder.
@type attr: L{SFTPAttributes}
@return: an SFTP error code like L{SFTP_OK}.
@rtype: int
"""
return SFTP_OP_UNSUPPORTED
def rmdir(self, path):
"""
Remove a directory if it exists. The C{path} should refer to an
existing, empty folder -- otherwise this method should return an
error.
@param path: requested path (relative or absolute) of the folder
to remove.
@type path: str
@return: an SFTP error code like L{SFTP_OK}.
@rtype: int
"""
return SFTP_OP_UNSUPPORTED
def chattr(self, path, attr):
"""
Change the attributes of a file. The C{attr} object will contain
only those fields provided by the client in its request, so you
should check for the presence of fields before using them.
@param path: requested path (relative or absolute) of the file to
change.
@type path: str
@param attr: requested attributes to change on the file.
@type attr: L{SFTPAttributes}
@return: an error code like L{SFTP_OK}.
@rtype: int
"""
return SFTP_OP_UNSUPPORTED
def canonicalize(self, path):
"""
Return the canonical form of a path on the server. For example,
if the server's home folder is C{/home/foo}, the path
C{"../betty"} would be canonicalized to C{"/home/betty"}. Note
the obvious security issues: if you're serving files only from a
specific folder, you probably don't want this method to reveal path
names outside that folder.
You may find the python methods in C{os.path} useful, especially
C{os.path.normpath} and C{os.path.realpath}.
The default implementation returns C{os.path.normpath('/' + path)}.
"""
if os.path.isabs(path):
out = os.path.normpath(path)
else:
out = os.path.normpath('/' + path)
if sys.platform == 'win32':
# on windows, normalize backslashes to sftp/posix format
out = out.replace('\\', '/')
return out
def readlink(self, path):
"""
Return the target of a symbolic link (or shortcut) on the server.
If the specified path doesn't refer to a symbolic link, an error
should be returned.
@param path: path (relative or absolute) of the symbolic link.
@type path: str
@return: the target path of the symbolic link, or an error code like
L{SFTP_NO_SUCH_FILE}.
@rtype: str I{or error code}
"""
return SFTP_OP_UNSUPPORTED
def symlink(self, target_path, path):
"""
Create a symbolic link on the server, as new pathname C{path},
with C{target_path} as the target of the link.
@param target_path: path (relative or absolute) of the target for
this new symbolic link.
@type target_path: str
@param path: path (relative or absolute) of the symbolic link to
create.
@type path: str
@return: an error code like C{SFTP_OK}.
@rtype: int
"""
return SFTP_OP_UNSUPPORTED

View File

@ -1,112 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
Exceptions defined by paramiko.
"""
class SSHException (Exception):
"""
Exception raised by failures in SSH2 protocol negotiation or logic errors.
"""
pass
class AuthenticationException (SSHException):
"""
Exception raised when authentication failed for some reason. It may be
possible to retry with different credentials. (Other classes specify more
specific reasons.)
@since: 1.6
"""
pass
class PasswordRequiredException (AuthenticationException):
"""
Exception raised when a password is needed to unlock a private key file.
"""
pass
class BadAuthenticationType (AuthenticationException):
"""
Exception raised when an authentication type (like password) is used, but
the server isn't allowing that type. (It may only allow public-key, for
example.)
@ivar allowed_types: list of allowed authentication types provided by the
server (possible values are: C{"none"}, C{"password"}, and
C{"publickey"}).
@type allowed_types: list
@since: 1.1
"""
allowed_types = []
def __init__(self, explanation, types):
AuthenticationException.__init__(self, explanation)
self.allowed_types = types
def __str__(self):
return SSHException.__str__(self) + ' (allowed_types=%r)' % self.allowed_types
class PartialAuthentication (AuthenticationException):
"""
An internal exception thrown in the case of partial authentication.
"""
allowed_types = []
def __init__(self, types):
AuthenticationException.__init__(self, 'partial authentication')
self.allowed_types = types
class ChannelException (SSHException):
"""
Exception raised when an attempt to open a new L{Channel} fails.
@ivar code: the error code returned by the server
@type code: int
@since: 1.6
"""
def __init__(self, code, text):
SSHException.__init__(self, text)
self.code = code
class BadHostKeyException (SSHException):
"""
The host key given by the SSH server did not match what we were expecting.
@ivar hostname: the hostname of the SSH server
@type hostname: str
@ivar key: the host key presented by the server
@type key: L{PKey}
@ivar expected_key: the host key expected
@type expected_key: L{PKey}
@since: 1.6
"""
def __init__(self, hostname, got_key, expected_key):
SSHException.__init__(self, 'Host key for server %s does not match!' % hostname)
self.hostname = hostname
self.key = got_key
self.expected_key = expected_key

File diff suppressed because it is too large Load Diff

View File

@ -1,299 +0,0 @@
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
Useful functions used by the rest of paramiko.
"""
from __future__ import generators
import array
from binascii import hexlify, unhexlify
import sys
import struct
import traceback
import threading
from paramiko.common import *
from paramiko.config import SSHConfig
# Change by RogerB - python < 2.3 doesn't have enumerate so we implement it
if sys.version_info < (2,3):
class enumerate:
def __init__ (self, sequence):
self.sequence = sequence
def __iter__ (self):
count = 0
for item in self.sequence:
yield (count, item)
count += 1
def inflate_long(s, always_positive=False):
"turns a normalized byte string into a long-int (adapted from Crypto.Util.number)"
out = 0L
negative = 0
if not always_positive and (len(s) > 0) and (ord(s[0]) >= 0x80):
negative = 1
if len(s) % 4:
filler = '\x00'
if negative:
filler = '\xff'
s = filler * (4 - len(s) % 4) + s
for i in range(0, len(s), 4):
out = (out << 32) + struct.unpack('>I', s[i:i+4])[0]
if negative:
out -= (1L << (8 * len(s)))
return out
def deflate_long(n, add_sign_padding=True):
"turns a long-int into a normalized byte string (adapted from Crypto.Util.number)"
# after much testing, this algorithm was deemed to be the fastest
s = ''
n = long(n)
while (n != 0) and (n != -1):
s = struct.pack('>I', n & 0xffffffffL) + s
n = n >> 32
# strip off leading zeros, FFs
for i in enumerate(s):
if (n == 0) and (i[1] != '\000'):
break
if (n == -1) and (i[1] != '\xff'):
break
else:
# degenerate case, n was either 0 or -1
i = (0,)
if n == 0:
s = '\000'
else:
s = '\xff'
s = s[i[0]:]
if add_sign_padding:
if (n == 0) and (ord(s[0]) >= 0x80):
s = '\x00' + s
if (n == -1) and (ord(s[0]) < 0x80):
s = '\xff' + s
return s
def format_binary_weird(data):
out = ''
for i in enumerate(data):
out += '%02X' % ord(i[1])
if i[0] % 2:
out += ' '
if i[0] % 16 == 15:
out += '\n'
return out
def format_binary(data, prefix=''):
x = 0
out = []
while len(data) > x + 16:
out.append(format_binary_line(data[x:x+16]))
x += 16
if x < len(data):
out.append(format_binary_line(data[x:]))
return [prefix + x for x in out]
def format_binary_line(data):
left = ' '.join(['%02X' % ord(c) for c in data])
right = ''.join([('.%c..' % c)[(ord(c)+63)//95] for c in data])
return '%-50s %s' % (left, right)
def hexify(s):
return hexlify(s).upper()
def unhexify(s):
return unhexlify(s)
def safe_string(s):
out = ''
for c in s:
if (ord(c) >= 32) and (ord(c) <= 127):
out += c
else:
out += '%%%02X' % ord(c)
return out
# ''.join([['%%%02X' % ord(c), c][(ord(c) >= 32) and (ord(c) <= 127)] for c in s])
def bit_length(n):
norm = deflate_long(n, 0)
hbyte = ord(norm[0])
if hbyte == 0:
return 1
bitlen = len(norm) * 8
while not (hbyte & 0x80):
hbyte <<= 1
bitlen -= 1
return bitlen
def tb_strings():
return ''.join(traceback.format_exception(*sys.exc_info())).split('\n')
def generate_key_bytes(hashclass, salt, key, nbytes):
"""
Given a password, passphrase, or other human-source key, scramble it
through a secure hash into some keyworthy bytes. This specific algorithm
is used for encrypting/decrypting private key files.
@param hashclass: class from L{Crypto.Hash} that can be used as a secure
hashing function (like C{MD5} or C{SHA}).
@type hashclass: L{Crypto.Hash}
@param salt: data to salt the hash with.
@type salt: string
@param key: human-entered password or passphrase.
@type key: string
@param nbytes: number of bytes to generate.
@type nbytes: int
@return: key data
@rtype: string
"""
keydata = ''
digest = ''
if len(salt) > 8:
salt = salt[:8]
while nbytes > 0:
hash_obj = hashclass.new()
if len(digest) > 0:
hash_obj.update(digest)
hash_obj.update(key)
hash_obj.update(salt)
digest = hash_obj.digest()
size = min(nbytes, len(digest))
keydata += digest[:size]
nbytes -= size
return keydata
def load_host_keys(filename):
"""
Read a file of known SSH host keys, in the format used by openssh, and
return a compound dict of C{hostname -> keytype ->} L{PKey <paramiko.pkey.PKey>}.
The hostname may be an IP address or DNS name. The keytype will be either
C{"ssh-rsa"} or C{"ssh-dss"}.
This type of file unfortunately doesn't exist on Windows, but on posix,
it will usually be stored in C{os.path.expanduser("~/.ssh/known_hosts")}.
Since 1.5.3, this is just a wrapper around L{HostKeys}.
@param filename: name of the file to read host keys from
@type filename: str
@return: dict of host keys, indexed by hostname and then keytype
@rtype: dict(hostname, dict(keytype, L{PKey <paramiko.pkey.PKey>}))
"""
from paramiko.hostkeys import HostKeys
return HostKeys(filename)
def parse_ssh_config(file_obj):
"""
Provided only as a backward-compatible wrapper around L{SSHConfig}.
"""
config = SSHConfig()
config.parse(file_obj)
return config
def lookup_ssh_host_config(hostname, config):
"""
Provided only as a backward-compatible wrapper around L{SSHConfig}.
"""
return config.lookup(hostname)
def mod_inverse(x, m):
# it's crazy how small python can make this function.
u1, u2, u3 = 1, 0, m
v1, v2, v3 = 0, 1, x
while v3 > 0:
q = u3 // v3
u1, v1 = v1, u1 - v1 * q
u2, v2 = v2, u2 - v2 * q
u3, v3 = v3, u3 - v3 * q
if u2 < 0:
u2 += m
return u2
_g_thread_ids = {}
_g_thread_counter = 0
_g_thread_lock = threading.Lock()
def get_thread_id():
global _g_thread_ids, _g_thread_counter, _g_thread_lock
tid = id(threading.currentThread())
try:
return _g_thread_ids[tid]
except KeyError:
_g_thread_lock.acquire()
try:
_g_thread_counter += 1
ret = _g_thread_ids[tid] = _g_thread_counter
finally:
_g_thread_lock.release()
return ret
def log_to_file(filename, level=DEBUG):
"send paramiko logs to a logfile, if they're not already going somewhere"
l = logging.getLogger("paramiko")
if len(l.handlers) > 0:
return
l.setLevel(level)
f = open(filename, 'w')
lh = logging.StreamHandler(f)
lh.setFormatter(logging.Formatter('%(levelname)-.3s [%(asctime)s.%(msecs)03d] thr=%(_threadid)-3d %(name)s: %(message)s',
'%Y%m%d-%H:%M:%S'))
l.addHandler(lh)
# make only one filter object, so it doesn't get applied more than once
class PFilter (object):
def filter(self, record):
record._threadid = get_thread_id()
return True
_pfilter = PFilter()
def get_logger(name):
l = logging.getLogger(name)
l.addFilter(_pfilter)
return l
class Counter (object):
"""Stateful counter for CTR mode crypto"""
def __init__(self, nbits, initial_value=1L, overflow=0L):
self.blocksize = nbits / 8
self.overflow = overflow
# start with value - 1 so we don't have to store intermediate values when counting
# could the iv be 0?
if initial_value == 0:
self.value = array.array('c', '\xFF' * self.blocksize)
else:
x = deflate_long(initial_value - 1, add_sign_padding=False)
self.value = array.array('c', '\x00' * (self.blocksize - len(x)) + x)
def __call__(self):
"""Increament the counter and return the new value"""
i = self.blocksize - 1
while i > -1:
c = self.value[i] = chr((ord(self.value[i]) + 1) % 256)
if c != '\x00':
return self.value.tostring()
i -= 1
# counter reset
x = deflate_long(self.overflow, add_sign_padding=False)
self.value = array.array('c', '\x00' * (self.blocksize - len(x)) + x)
return self.value.tostring()
def new(cls, nbits, initial_value=1L, overflow=0L):
return cls(nbits, initial_value=initial_value, overflow=overflow)
new = classmethod(new)

View File

@ -1,143 +0,0 @@
# Copyright (C) 2005 John Arbash-Meinel <john@arbash-meinel.com>
# Copyright 2012 Citrix Systems, Inc. Licensed under the
# Apache License, Version 2.0 (the "License"); you may not use this
# file except in compliance with the License. Citrix Systems, Inc.
# reserves all rights not expressly granted by the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Automatically generated by addcopyright.py at 04/03/2012
# Modified up by: Todd Whiteman <ToddW@ActiveState.com>
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
Functions for communicating with Pageant, the basic windows ssh agent program.
"""
import os
import struct
import tempfile
import mmap
import array
# ctypes is part of standard library since Python 2.5
_has_win32all = False
_has_ctypes = False
try:
# win32gui is preferred over win32ui to avoid MFC dependencies
import win32gui
_has_win32all = True
except ImportError:
try:
import ctypes
_has_ctypes = True
except ImportError:
pass
_AGENT_COPYDATA_ID = 0x804e50ba
_AGENT_MAX_MSGLEN = 8192
# Note: The WM_COPYDATA value is pulled from win32con, as a workaround
win32con_WM_COPYDATA = 74
def _get_pageant_window_object():
if _has_win32all:
try:
hwnd = win32gui.FindWindow('Pageant', 'Pageant')
return hwnd
except win32gui.error:
pass
elif _has_ctypes:
# Return 0 if there is no Pageant window.
return ctypes.windll.user32.FindWindowA('Pageant', 'Pageant')
return None
def can_talk_to_agent():
"""
Check to see if there is a "Pageant" agent we can talk to.
This checks both if we have the required libraries (win32all or ctypes)
and if there is a Pageant currently running.
"""
if (_has_win32all or _has_ctypes) and _get_pageant_window_object():
return True
return False
def _query_pageant(msg):
hwnd = _get_pageant_window_object()
if not hwnd:
# Raise a failure to connect exception, pageant isn't running anymore!
return None
# Write our pageant request string into the file (pageant will read this to determine what to do)
filename = tempfile.mktemp('.pag')
map_filename = os.path.basename(filename)
f = open(filename, 'w+b')
f.write(msg )
# Ensure the rest of the file is empty, otherwise pageant will read this
f.write('\0' * (_AGENT_MAX_MSGLEN - len(msg)))
# Create the shared file map that pageant will use to read from
pymap = mmap.mmap(f.fileno(), _AGENT_MAX_MSGLEN, tagname=map_filename, access=mmap.ACCESS_WRITE)
try:
# Create an array buffer containing the mapped filename
char_buffer = array.array("c", map_filename + '\0')
char_buffer_address, char_buffer_size = char_buffer.buffer_info()
# Create a string to use for the SendMessage function call
cds = struct.pack("LLP", _AGENT_COPYDATA_ID, char_buffer_size, char_buffer_address)
if _has_win32all:
# win32gui.SendMessage should also allow the same pattern as
# ctypes, but let's keep it like this for now...
response = win32gui.SendMessage(hwnd, win32con_WM_COPYDATA, len(cds), cds)
elif _has_ctypes:
_buf = array.array('B', cds)
_addr, _size = _buf.buffer_info()
response = ctypes.windll.user32.SendMessageA(hwnd, win32con_WM_COPYDATA, _size, _addr)
else:
response = 0
if response > 0:
datalen = pymap.read(4)
retlen = struct.unpack('>I', datalen)[0]
return datalen + pymap.read(retlen)
return None
finally:
pymap.close()
f.close()
# Remove the file, it was temporary only
os.unlink(filename)
class PageantConnection (object):
"""
Mock "connection" to an agent which roughly approximates the behavior of
a unix local-domain socket (as used by Agent). Requests are sent to the
pageant daemon via special Windows magick, and responses are buffered back
for subsequent reads.
"""
def __init__(self):
self._response = None
def send(self, data):
self._response = _query_pageant(data)
def recv(self, n):
if self._response is None:
return ''
ret = self._response[:n]
self._response = self._response[n:]
if self._response == '':
self._response = None
return ret
def close(self):
pass

View File

@ -7485,7 +7485,7 @@ div.panel.ui-dialog div.list-view div.fixed-header {
color: #FFFFFF;
background: url(../images/buttons.png) no-repeat -457px -503px;
font-size: 11px;
padding: 6px 24px 6px 9px;
padding: 6px 17px 6px 9px;
/*+text-shadow:0px 1px 1px #395065;*/
-moz-text-shadow: 0px 1px 1px #395065;
-webkit-text-shadow: 0px 1px 1px #395065;

View File

@ -804,6 +804,9 @@
success: function(json) {
var item = json.updateuserresponse.user;
args.response.success({data:item});
},
error: function(data) {
args.response.error(parseXMLHttpResponse(data));
}
});